📄 dhcpclnt.c
字号:
/*
* FILENAME: dhcpclnt.c
*
* Copyright 1997- 2000 By InterNiche Technologies Inc. All rights reserved
*
* DHCP Client code for InterNiche IP stack.
*
* MODULE: INET
*
* ROUTINES: dhc_get_srv_ipaddr(), dhc_init(),
* ROUTINES: dhc_set_callback(), dhc_upcall(), dhc_buildheader(),
* ROUTINES: dhc_discover(), dhc_rx_offer(), dhc_request(), dhc_setip(),
* ROUTINES: dhc_resetip(), dhc_decline(), dh_getlong(), dhc_extract_opts(),
* ROUTINES: dhc_stats(), dhc_second(), dhc_halt(), dhc_reclaim(),
* ROUTINES: dhc_state_init(), dhc_alldone(), dhc_ifacedone(),
* ROUTINES: dhc_set_state(),
*
* PORTABLE: yes
*/
/* Additional Copyrights: */
/* Portions Copyright 1996 by NetPort Software. */
#include "ipport.h"
#ifdef DHCP_CLIENT /* ifdef out whole file */
#include "q.h"
#include "netbuf.h"
#include "net.h"
#include "ip.h"
#include "udp.h"
#include "dhcpclnt.h"
#include "minip.h" /* (yaxon add) */
/* A lot of the following variables are named similarly to variables
* of similar function in the dhcp server code. n general the
* difference is that the client names start with "dhc_" and the
* server versions
*/
UDPCONN dhc_conn = NULL;
long xids = 0x22334455; /* seed for unique request IDs */
/* DHCP client statistics: */
ulong dsc_errors = 0; /* protocol/implementation runtime errors */
ulong dsc_discovers = 0; /* discovers sent */
ulong dsc_offers = 0; /* offers recieved */
ulong dsc_requests = 0; /* requests sent */
ulong dsc_acks = 0; /* acks received */
ulong dsc_bpreplys = 0; /* plain bootp replys received */
ulong dsc_declines = 0; /* declines sent */
ulong dsc_releases = 0; /* releases sent */
ulong dsc_naks = 0; /* naks received */
ulong dsc_renew = 0; /* unicast requests sent to renew lease */
ulong dsc_rebind = 0; /* broadcast requests sent to renew lease */
ulong dsc_rlyerrs = 0; /* runtime errors due to DHCP Relay agents */
/* reqlist contains the list of options to be requested in a DISCOVER
* pkt. reqlist is used only if DHCP_REQLIST is enabled.
*/
#ifdef DHCP_REQLIST
u_char reqlist[] = { DHOP_SNMASK,DHOP_ROUTER, DHOP_DNSRV, DHOP_DOMAIN } ;
/* u_char reqlist[] = { DHOP_SNMASK,DHOP_ROUTER, DHOP_DNSRV,
* DHOP_DOMAIN,0x2c,0x2e,0x2f };
*/
int reqlist_len = sizeof(reqlist)/sizeof(u_char);
#endif /* DHCP_REQLIST */
struct dhc_state dhc_states[MAXNETS]; /* DHCP client state of each net. */
#ifdef NET_STATS
static char *dhc_state_str[] =
{
"unused",
"init",
"init-reboot",
"rebooting",
"selecting",
"requesting",
"bound",
"renewing",
"rebinding",
"restarting",
};
#endif
#define DHC_BCASTFLAG 0x8000 /* for the flags field of pkt */
#define DHC_INFINITY 0xffffffff /* That is, "-1" */
#define DHC_MAX_TRIES 4 /* Max num of retires to tbe done */
#define DHC_RETRY_TMO 4 /* Timeout(secs) for retries */
#define DHCPDATA ((void*)0xFFFFFFFD) /* tag for pass to udp_open() */
/* DHCP functions used within this file */
int dhc_upcall (PACKET pkt, void * data);
int dhc_discover (int iface); /* send a dhcp discover packet */
int dhc_reclaim (int iface); /* try to reclaim previous address */
int dhc_setip (int iface); /* set iface's IP addr */
int dhc_resetip (int iface); /* reset iface's IP addr */
int dhc_request (int iface, int xid_flag); /* Send a request */
int dhc_rx_offer (int iface, struct bootp * bp, unsigned bplen);
int dhc_decline (int iface, struct bootp * bp, unsigned bplen);
int dhc_buildheader (int iface, struct bootp * outbp);
int dhc_extract_opts(int iface, u_char * opts);
void dhc_set_state (int iface, int state);
extern void fixup_subnet_mask(int netnum); /* from ipnet.c */
static long dh_getlong( u_char *ptr );
extern char * dhc_hostname(void); /* returns a name for this machine */
/* FUNCTION: dhc_get_srv_ipaddr()
*
* PARAM1: u_char *options - the ones after "magic cookie"
*
* RETURNS:
*/
ip_addr
dhc_get_srv_ipaddr(u_char *options /* after magic cookie */)
{
u_char * opts;
u_char optlen;
ip_addr srv_ipaddr = 0;
if ((opts = find_opt(DHOP_SERVER, options)) != NULL)
{
opts++;
optlen = *opts;
opts++;
srv_ipaddr = dh_getlong(opts);
opts += optlen;
}
return (srv_ipaddr);
}
/* dhc_init() - this must set up the default fields above, read in
* bootptab file (if supported) and open UDP socket for listens.
* Returns 0 if OK, else negative error code from net.h file
*/
/* FUNCTION: dhc_init()
*
* This must set up the default fields above, read in
* bootptab file (if supported) and open UDP socket for listens.
*
* PARAM1: void
*
* RETURNS: Returns 0 if OK, else negative error code from net.h file
*/
int
dhc_init(void)
{
int i;
/* open UDP connection to receive incoming DHCP replys */
dhc_conn = udp_open(0L, /* wildcard foriegn host */
BOOTP_SERVER_PORT, BOOTP_CLIENT_PORT,
dhc_upcall, DHCPDATA);
if (!dhc_conn)
return ENP_RESOURCE;
for (i = 0; i < MAXNETS; i++)
{
dhc_states[i].state = DHCS_UNUSED;
dhc_states[i].tries = 0;
}
return 0;
}
/* FUNCTION: dhc_set_callback()
*
* dhc_set_callback() : This interface allows the caller to set a
* calling back for a interface. When DHCP Client gets an ACK, it
* callings this routine. This mechanism is provider so that the
* caller can do some processing when the interface is up (like doing
* initializations or blinking LEDs , etc.).
*
*
* PARAM1: int iface
* PARAM2: int (*routine)(int
* PARAM3: int
*
* RETURNS:
*/
void
dhc_set_callback(int iface, int (*routine)(int,int) )
{
dhc_states[iface].callback = routine;
}
/* FUNCTION: dhc_upcall()
*
* dhc_upcall() - DHCP client UDP callback. Called from stack
* whenever we get a bootp (or DHCP) reply. Returns 0 or error code.
* .
*
* PARAM1: PACKET pkt
* PARAM2: void * data
*
* RETURNS: If the processing was sucessfull, the packet is freed and 0 is
* returned. Otherwise the packet is NOT free'ed and an error code is
* returned
*/
int
dhc_upcall(PACKET pkt, void * data)
{
struct bootp * bp;
int len = pkt->nb_plen; /* len of UDP data - the bootp/dhcp struct */
int dhcptype = 0; /* DHCP type - not valid if bootp */
int e;
int iface;
u_char * opts; /* scratch options pointer */
if (data != DHCPDATA)
{
dtrap("dhcpclnt 0\n");
return ENP_LOGIC; /* internal logic error */
}
/* punt if packet didn't come in a net we sent on */
iface = net_num(pkt->net);
if (dhc_states[iface].state == DHCS_UNUSED)
return ENP_NOT_MINE;
bp = (struct bootp *)pkt->nb_prot;
/* Validate various fields */
if ((len < (sizeof(struct bootp)-BOOTP_OPTSIZE) ) ||
(bp->op != BOOTREPLY) ||
(*(u_long*)(&bp->options) != RFC1084_MAGIC_COOKIE))
{
dtrap("dhcpclnt 1\n");
dsc_errors++;
return ENP_NOT_MINE;
}
/* punt offers or replys which are not for me */
if(MEMCMP(bp->chaddr, pkt->net->n_haddr, pkt->net->n_hal))
return ENP_NOT_MINE; /* not an error, just ignore it */
/* see if it's full DHCP or plain bootp by looking for dhcp type option */
opts = find_opt(DHOP_TYPE ,&bp->options[4]);
if (opts && *opts == DHOP_TYPE)
{
dhcptype = *(opts+2);
bp->op |= ISDHCP; /* tag packet for isdhcp() macro */
}
if (isdhcp(bp))
{
switch (dhcptype)
{
case DHCP_DISCOVER:
case DHCP_REQUEST:
case DHCP_DECLINE:
case DHCP_RELEASE:
dsc_errors++; /* these should only be upcalled to a server */
return ENP_NOT_MINE;
}
switch (dhc_states[iface].state)
{
case DHCS_INIT:
case DHCS_INITREBOOT:
/* How can we receive any response when we never sent one */
case DHCS_BOUND:
/* If there are multiple DHCP Servers, and one of them is slow
in responding, we might get OFFER pkts when are in BOUND state */
dsc_errors++; /* these should only be upcalled to a server */
return ENP_NOT_MINE;
case DHCS_SELECTING:
/* We will respond to the first offer packet that we receive ) */
if ( dhcptype == DHCP_OFFER ) /* got offer back from server */
{
dsc_offers++;
dhc_states[iface].srv_ipaddr = dhc_get_srv_ipaddr(&bp->options[4]);
if (dhc_states[iface].srv_ipaddr == 0 )
{
dtrap("dhcpclnt 2\n"); /* didn't receive server-identifier option */
dsc_errors++;
dhc_states[iface].srv_ipaddr = pkt->fhost; /* Try using fhost */
}
if (bp->hops)
{
/* OFFER is received via DHCP Relay Agent. Remember the
* IP addr of DHCP Relay Agent, so that packets from other
* DHCP Relay Agents can be discarded
*/
dhc_states[iface].rly_ipaddr = pkt->fhost; /* Try using fhost */
}
else
dhc_states[iface].rly_ipaddr = 0;
e = dhc_rx_offer(iface,bp,pkt->nb_plen); /* send request */
if (e)
{
dsc_errors++;
dhc_set_state(iface,DHCS_INIT);
dtrap("dhcpclnt 3\n");
return ENP_NOT_MINE;
}
else
dhc_set_state(iface,DHCS_REQUESTING);
}
else
{
/* We can't receive any other packet in this state.
* Report an error and remain in SELECTING state, so that
* an OFFER packet from another DHCP server can be
* accepted. If we timeout waiting for a OFFER packet,
* then dhc_second() will transition to DHCS_INIT state.
*/
dsc_errors++;
if ( dhcptype == DHCP_NAK )
dsc_naks++;
return ENP_NOT_MINE;
}
break;
case DHCS_REQUESTING:
case DHCS_REBINDING:
case DHCS_RENEWING:
/* If the ACK/NACK is not from the same server which sent
* the OFFER packet, then discard it. in DHCS_REBOOTING
* state, srv_ipaddr is 0. Hence don't check in that state
*/
if ( dhc_states[iface].srv_ipaddr !=
dhc_get_srv_ipaddr(&bp->options[4]) )
{
dsc_errors++;
return ENP_NOT_MINE;
}
if (dhc_states[iface].rly_ipaddr &&
(dhc_states[iface].rly_ipaddr != pkt->fhost))
{
dsc_rlyerrs++;
dsc_errors++;
return ENP_NOT_MINE;
}
case DHCS_REBOOTING:
if ( dhcptype == DHCP_ACK ) /* Server OKed our request */
{
dsc_acks++;
dhc_extract_opts(iface,&bp->options[4]);
if ( dhc_states[iface].lease == DHC_INFINITY )
{
dhc_states[iface].t1 = DHC_INFINITY ;
dhc_states[iface].t2 = DHC_INFINITY ;
}
else
{
dhc_states[iface].t1 = dhc_states[iface].lease/2 ;
dhc_states[iface].t2 = (dhc_states[iface].lease/8)*7 ;
}
dhc_states[iface].lease_start = cticks; /* to calc lease expiry */
dhc_states[iface].srv_ipaddr = dhc_get_srv_ipaddr(&bp->options[4]);
if (dhc_states[iface].srv_ipaddr == 0 )
{
dtrap("dhcpclnt 4\n"); /* didn't receive server-identifier option */
dsc_errors++;
dhc_states[iface].srv_ipaddr = pkt->fhost; /* Try using fhost */
}
if (bp->hops)
{
/* OFFER is received via DHCP Relay Agent. Remember the
* IP addr of DHCP Relay Agent, so that packets from other
* DHCP Relay Agents can be discarded
*/
dhc_states[iface].rly_ipaddr = pkt->fhost; /* Try using fhost */
}
else
dhc_states[iface].rly_ipaddr = 0;
dhc_setip(iface);
dhc_set_state(iface,DHCS_BOUND);
}
else if ( dhcptype == DHCP_NAK ) /* Server denied our request */
{
dhc_set_state(iface,DHCS_INIT);
dsc_naks++;
}
else
{
/* We can't receive any other packet in this state.
Revert to INIT state. */
dsc_errors++;
dhc_set_state(iface,DHCS_INIT);
dtrap("dhcpclnt 5\n");
return ENP_NOT_MINE;
}
break;
default: /* bad state */
dtrap("dhcpclnt 6\n");
dhc_set_state(iface,DHCS_INIT);
dsc_errors++;
return -1;
}
}
else /* plain bootp reply */
{
dsc_bpreplys++;
dhc_extract_opts(iface,&bp->options[4]);
dhc_states[iface].ipaddr = bp->yiaddr;
dhc_setip(iface);
/* Set values so that DHCP State Machine remains happy */
dhc_set_state(iface,DHCS_BOUND);
dhc_states[iface].t1 = DHC_INFINITY ;
}
udp_free(pkt);
return 0;
}
/* FUNCTION: dhc_buildheader()
*
* dhc_buildheader() - build BOOTP request header
*
*
* PARAM1: int iface
* PARAM2: struct bootp * outbp
*
* RETURNS: Returns 0 on success, else an ENP_ error code.
*/
int
dhc_buildheader(int iface, struct bootp * outbp)
{
int addrlen; /* length of hardware address */
MEMSET(outbp, 0, sizeof(struct bootp)); /* most of this is 0 anyway */
outbp->op = BOOTREQUEST;
/* map SNMPish hardware types into bootp types */
switch (nets[iface]->n_mib->ifType)
{
case ETHERNET: /* ETHERNET defined in net.h */
outbp->htype = ETHHWTYPE; /* defined in dhcp.h */
break;
case PPP:
case SLIP:
outbp->htype = LINEHWTYPE; /* line type for PPP or SLIP */
break;
default:
dtrap("dhcpclnt 7\n");
return ENP_LOGIC; /* this shouldn't happen */
}
addrlen = min(16, nets[iface]->n_hal);
outbp->hlen = (u_char)addrlen;
outbp->hops = 0;
if(dhc_states[iface].state == DHCS_RENEWING)
outbp->flags = 0; /* Renewing needs unicast */
else
outbp->flags = htons(DHC_BCASTFLAG); /* Othwise broadcast */
outbp->xid = dhc_states[iface].xid;
outbp->secs = dhc_states[iface].secs;
#ifdef NPDEBUG
/* make sure net[] has a MAC address, even if length is zero */
if(nets[iface]->n_haddr == NULL)
{
dtrap("dhcpclnt 8\n");
return ENP_LOGIC;
}
#endif
MEMCPY(outbp->chaddr, nets[iface]->n_haddr, addrlen);
/* return success */
return 0;
}
unsigned long sysuptime(void);
/* FUNCTION: dhc_discover()
*
* dhc_discover() - Send initial DHCP discovery packet for the passed
* interface.
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -