📄 dhcpc.c
字号:
/*
*Copyright (c) 2000-2002 Viola Systems Ltd.
*All rights reserved.
*
*Redistribution and use in source and binary forms, with or without
*modification, are permitted provided that the following conditions
*are met:
*
*1. Redistributions of source code must retain the above copyright
*notice, this list of conditions and the following disclaimer.
*
*2. Redistributions in binary form must reproduce the above copyright
*notice, this list of conditions and the following disclaimer in the
*documentation and/or other materials provided with the distribution.
*
*3. The end-user documentation included with the redistribution, if
*any, must include the following acknowledgment:
* "This product includes software developed by Viola
* Systems (http://www.violasystems.com/)."
*
*Alternately, this acknowledgment may appear in the software itself,
*if and wherever such third-party acknowledgments normally appear.
*
*4. The names "OpenTCP" and "Viola Systems" must not be used to
*endorse or promote products derived from this software without prior
*written permission. For written permission, please contact
*opentcp@opentcp.org.
*
*5. Products derived from this software may not be called "OpenTCP",
*nor may "OpenTCP" appear in their name, without prior written
*permission of the Viola Systems Ltd.
*
*THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
*WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
*MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
*IN NO EVENT SHALL VIOLA SYSTEMS LTD. OR ITS CONTRIBUTORS BE LIABLE
*FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
*CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
*SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
*BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
*WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
*OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
*EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*====================================================================
*
*OpenTCP is the unified open source TCP/IP stack available on a series
*of 8/16-bit microcontrollers, please see <http://www.opentcp.org>.
*
*For more information on how to network-enable your devices, or how to
*obtain commercial technical support for OpenTCP, please see
*<http://www.violasystems.com/>.
*/
/** \file dhcpc.c
* \brief OpenTCP DHCP client implementation
* \author
* \li Vladan Jovanovic (vladan.jovanovic@violasystems.com)
* \version 1.03
* \date 23.5.2003
* \bug
* \warning
* \todo
* \li We SHOULD first test (with PING or ARP) assigned IP
* address to see if it's in use already.
*
* OpenTCP DHCP client protocol implementation. Features a complete
* DHCP state machine. Function declarations can be found in dhcpc.h
*/
#include"datatypes.h"
#include"debug.h"
#include"system.h"
#include"tcp_ip.h"
#ifdef DHCPC_WANTED
#include"timers.h"
#include"dhcpc.h"
/** \brief Holds DHCP clients' state information
*
* This variable holds DHCP clients' current state information. Possible
* states are DHCP_STATE_INIT_REBOOT, DHCP_STATE_REBOOTING,
* DHCP_STATE_INIT, DHCP_STATE_SELECTING, DHCP_STATE_REQUESTING,
* DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING.
*/
UINT8 dhcpc_state;
/** \brief DHCP client's timer handle
*
* Hold DHCP clients' timer handle. We'll use only one timer from
* timer pool and take care of the rest by ourselves manually
*/
UINT8 dhcpc_timer_handle;
/** \brief DHCP client's UDP socket handle
*
* DHCP client's UDP socket handle
*/
INT8 dhcpc_soc_handle;
/** \brief Holds information if DHCP client is initialized
*
* Holds information if DHCP client is initialized
*/
UINT8 dhcpc_initialized=0;
/** \brief DHCP renew timer
*
* This variable holds renew time (in seconds) after which we'll
* start the renewing process. While obtaining the parameters from
* DHCP server (thus before we know of the renew time) this is
* used also to time retransmissions.
*/
UINT32 dhcpc_t1;
/** \brief DHCP rebind timer
*
* This variable holds rebind time (in seconds) after which we'll
* start the rebinding process. While obtaining the paramters from
* DHCP server (thus before we know of the renew time) this is
* also used to time retransmissions as well as timeout detection
*/
UINT32 dhcpc_t2;
/** \brief DHCP server identifier as received from DHCP server
*
* This variable will hold DHCP server identifier (which will actually
* be server's IP address).
*/
UINT32 dhcpc_server_identifier;
/** \brief Holds offered IP address or IP address that we're requesting
*
* This variable holds the IP address that DHCP server offered to us
* during address request procedure and this is the address that we
* will be requesting in all future requests untill DHCP server
* disallows us to use it any more.
*/
UINT32 dhcpc_requested_ip;
INT32 dhcpc_eventlistener(INT8 cbhandle, UINT8 event, UINT32 ipaddr, UINT16 port, UINT16 buffindex, UINT16 datalen);
INT8 dhcpc_send_message(UINT8 msg_type);
/** \brief Initializes DHCP client
* \author
* \li Vladan Jovanovic (vladan.jovanovic@violasystems.com)
* \date 23.05.2003
*
* This function should be called once when system starts to initialize
* and start DHCP client. Before this function is invoked,
* localmachine.localip MUST be se to either zero (in which case DHCP
* client will request any IP address) or a previously assigned IP
* address (which doesn't mean DHCP server will allow us to continue
* using this address) in which case DHCP client will first try to
* obtain that existing IP address.
*/
INT8 dhcpc_init(void)
{
/* If we already have some IP address (stored from some previous
* requests, try obtaining it again */
if((localmachine.localip!=0)&&(localmachine.localip!=0xffffffff)){
dhcpc_state=DHCP_STATE_INIT_REBOOT;
}else{
/* start from beginning */
dhcpc_state=DHCP_STATE_INIT;
}
dhcpc_soc_handle=udp_getsocket(0,dhcpc_eventlistener,UDP_OPT_SEND_CS|UDP_OPT_CHECK_CS);
if(dhcpc_soc_handle<0){
DEBUGOUT("DHCP client not able to obtain socket!\r\n");
return (-1);
}
/* open socket for communication immediately */
(void)udp_open(dhcpc_soc_handle,DHCP_CLIENT_PORT);
/* get timer handle */
dhcpc_timer_handle=get_timer();
/* initialize timer for 1 sec intervals */
init_timer(dhcpc_timer_handle,TIMERTIC*1); /* on every second */
dhcpc_initialized=1;
DEBUGOUT("DHCP Client initialized\r\n");
return (0);
}
/** \brief DHCP client main state machine
* \author
* \li Vladan Jovanovic (vladan.jovanovic@violasystems.com)
* \date 23.05.2003
*
* Call this function periodically from main loop to ensure
* proper operation. This function holds the main state machine
* of DHCP client that ensures proper operation.
*/
void dhcpc_run(void){
UINT8 sec=0;
if(!dhcpc_initialized)
return;
if(!check_timer(dhcpc_timer_handle))
sec=1;
switch(dhcpc_state){
case DHCP_STATE_INIT_REBOOT:
dhcpc_state=DHCP_STATE_REBOOTING;
dhcpc_t1=4; /* next retransmission in 4 secs */
dhcpc_t2=8; /* after that after 8 secs retrs */
dhcpc_requested_ip=localmachine.localip;
(void)dhcpc_send_message(DHCP_REQUEST);
break;
case DHCP_STATE_REBOOTING:
if(sec)
dhcpc_t1--;
if(!dhcpc_t1){
if(dhcpc_t2>=32){
/* we need to restart, we don't want to wait more
* than 30 secs for retransmissions
*/
dhcpc_state=DHCP_STATE_INIT_REBOOT;
}else{
dhcpc_t1=dhcpc_t2;
dhcpc_t2<<=1; /* exponential backoff retransmission */
(void)dhcpc_send_message(DHCP_REQUEST);
}
}
break;
case DHCP_STATE_INIT:
/* switch to selecting, send DHCPDISCOVER and initialize
* timeout timer
*/
DEBUGOUT("DHCP State=INIT; Sending DHCPDISCOVER message; State INIT-->SELECTING\r\n");
dhcpc_state=DHCP_STATE_SELECTING;
(void)dhcpc_send_message(DHCP_DISCOVER);
/* T1 will be timeout timer. T2 will hold next timeout value */
dhcpc_t1=4; /* next retransmission after 4 secs */
dhcpc_t2=8; /* after 4 secs expire, this will be next timeout */
break;
case DHCP_STATE_SELECTING:
/* one second expired ? */
if(sec)
dhcpc_t1--;
/* timeout expired without receiving DHCPOFFER ? */
if(!dhcpc_t1){
DEBUGOUT("DHCP State=SELECTING; ");
/* yes, retransmit or restart the process */
if(dhcpc_t2>=32){
/* we need to restart, we don't want to wait more
* than 30 secs for retransmissions
*/
dhcpc_state=DHCP_STATE_INIT;
DEBUGOUT("Timeout for retransmissions too big; State SELECTING-->INIT\r\n");
}else{
DEBUGOUT("Retransmitting DHCPDISCOVER\r\n");
dhcpc_t1=dhcpc_t2;
dhcpc_t2<<=1; /* exponential backoff retransmission */
(void)dhcpc_send_message(DHCP_DISCOVER);
}
}
break;
case DHCP_STATE_REQUESTING:
/* DHCPREQUEST sent, waiting for proper response. Retransmit
* if necessary
*/
if(sec)
dhcpc_t1--;
if(!dhcpc_t1){
/* timeout occured without receiving DHCPACK */
DEBUGOUT("DHCP State=REQUESTING; ");
if(dhcpc_t2>=32){
/* we need to restart, we don't want to wait more
* than 30 secs for retransmissions. Restart whole
* process
*/
dhcpc_state=DHCP_STATE_INIT;
DEBUGOUT("Timeout for retransmits too big; State REQUESTING-->INIT\r\n");
}else{
DEBUGOUT("Retransmitting DHCPREQUEST\r\n");
dhcpc_t1=dhcpc_t2;
dhcpc_t2<<=1; /* exponential backoff retransmission */
(void)dhcpc_send_message(DHCP_REQUEST);
}
}
break;
case DHCP_STATE_BOUND:
/* wait for T1 to expire */
if(sec){
dhcpc_t1--;
dhcpc_t2--;
}
if(!dhcpc_t1){
DEBUGOUT("DHCP State=BOUND; Starting renewing process; State BOUND-->RENEWING\r\n");
/* T1 expired, start renewing process */
dhcpc_state=DHCP_STATE_RENEWING;
(void)dhcpc_send_message(DHCP_REQUEST);
/* T1 will be used for retransmissions untill we
* return to BOUND state or reset to INIT state
*/
dhcpc_t1=10; /* fixed 10sec retransmissions */
}
break;
case DHCP_STATE_RENEWING:
if(sec){
dhcpc_t1--;
dhcpc_t2--;
}
if(!dhcpc_t2){
DEBUGOUT("DHCP State=RENEWING; T2 expired; State RENEWING-->REBINDING\r\n");
/* oh no, T2 also expired. switch to rebinding state. This
* is our last attempt to retain this IP address.
*/
dhcpc_state=DHCP_STATE_REBINDING;
(void)dhcpc_send_message(DHCP_REQUEST);
/* dhcpc_t1 will be used for timeouts */
dhcpc_t1=5; /* 5 second retransmits */
/* dhcpc_t2 will be used for fixed numer of retries. This
* is a bit different than per RFC, but we don't want
* another 32-bit timer value for keeping lease time.
*/
dhcpc_t2=10;
}else
if(!dhcpc_t1){
DEBUGOUT("DHCP State=RENEWING; Retransmitting DHCPREQUEST\r\n");
/* retransmit DHCP REQUEST messages */
(void)dhcpc_send_message(DHCP_REQUEST);
dhcpc_t1=10;
}
break;
case DHCP_STATE_REBINDING:
if(sec)
dhcpc_t1--;
if(!dhcpc_t1){
dhcpc_t1=5; /* 5 second retransmits */
dhcpc_t2--; /* retransmit count */
if(!dhcpc_t2){
DEBUGOUT("DHCP State=REBINDING; Lease time expired; State REBINDING-->INIT\r\n");
/* used up retransmission. Assume that lease time
* expired by now. Restart the process
*/
dhcpc_state=DHCP_STATE_INIT;
localmachine.localip=0; /* can't use this any more */
}else{
DEBUGOUT("DHCP State=REBINDING; Retransmitting DHCPREQUEST\r\n");
/* still have some time */
(void)dhcpc_send_message(DHCP_REQUEST);
}
}
break;
default:
break;
}
if(sec){
init_timer(dhcpc_timer_handle,TIMERTIC*1);
}
}
/** \brief Sends DHCP messages
* \author
* \li Vladan Jovanovic (vladan.jovanovic@violasystems.com)
* \date 23.05.2003
* \param msg_type Type of DHCP message to be sent. This implementation
* can send only #DHCP_DISCOVER, #DHCP_REQUEST and #DCHP_DECLINE
* messages.
* \return Returns result of udp_send() function.
*
* This is internal function invoked to send appropriate DHCP message.
*/
INT8 dhcpc_send_message(UINT8 msg_type)
{
UINT16 index;
UINT8 *buf_ptr; /* transmit buffer pointer */
/* first clear transmit buffer to all zeroes */
for(index=UDP_APP_OFFSET;index<NETWORK_TX_BUFFER_SIZE;index++)
net_buf[index]=0;
buf_ptr=net_buf+UDP_APP_OFFSET;
/* create DHCP message */
*buf_ptr++=BOOT_REQUEST;
*buf_ptr++=0x01; /* htype=ethernet */
*buf_ptr++=0x06; /* hlen=6 for ethernet */
*buf_ptr++=0x00; /* hops=0 by clients */
/* xid, use constant value for all requests (allowed by RFC) */
*buf_ptr++=0xAA;
*buf_ptr++=0xBB;
*buf_ptr++=0xCC;
*buf_ptr++=0xDD;
/* seconds from boot. Fixed for now */
*buf_ptr++=0x00;
*buf_ptr++=0x00;
/* flags, use broadcast */
*buf_ptr++=0x80;
*buf_ptr++=0x00;
/* ciaddr. Sent only if client is in BOUND, RENEW or REBINDING
* state (RFC2131)
*/
if((dhcpc_state==DHCP_STATE_BOUND)
||(dhcpc_state==DHCP_STATE_RENEWING)
||(dhcpc_state==DHCP_STATE_REBINDING)){
*buf_ptr++=(UINT8)(localmachine.localip>>24);
*buf_ptr++=(UINT8)(localmachine.localip>>16);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -