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

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