📄 dhcpclnt.c
字号:
*
* PARAM1: int iface
*
* RETURNS: Returns 0 if ok, else non-zero ENP_ error.
*/
int
dhc_discover(int iface)
{
PACKET pkt;
struct bootp * outbp; /* pointer to bootp block in buffer to build */
u_char * opts; /* scratch pointer to DHCP options field */
long leasetime;
int e;
/* get a UDP packet buffer for DHCP sending */
pkt = udp_alloc(sizeof(struct bootp), 0);
if (!pkt)
return ENP_NOMEM;
pkt->nb_plen = sizeof(struct bootp);
/* start a new DHCP transaction */
dhc_states[iface].xid = xids++;
dhc_states[iface].secs = (unsigned short)(sysuptime()/100L);
/* set up DHCP/BOOTP header in buffer */
outbp = (struct bootp *)pkt->nb_prot; /* overlay bootp struct on buffer */
e = dhc_buildheader(iface,outbp);
if (e)
return e;
/* and turn it into a DHCP DISCOVER packet */
*(long*)(&outbp->options) = RFC1084_MAGIC_COOKIE;
opts = &outbp->options[4]; /* encode options after cookie */
*opts++ = DHOP_TYPE;
*opts++ = 1; /* length of option field */
*opts++ = DHCP_DISCOVER;
leasetime = -1L ; /* ask for infinite lease */
PUT_IP_OPT(opts, DHOP_LEASE, leasetime);
/* if we already have an IP address, try to get it from the server */
if (nets[iface]->n_ipaddr != 0)
{
ip_addr my_ip = htonl(nets[iface]->n_ipaddr);
PUT_IP_OPT(opts, DHOP_CADDR, my_ip);
}
/* If there is a list of options to be requested from server, include it*/
#ifdef DHCP_REQLIST
if ( reqlist_len > 0 )
{
int i;
*opts++ = DHOP_REQLIST ;
*opts++ = (u_char)reqlist_len ;
for (i=0 ; i < reqlist_len ; i++ )
*opts++ = reqlist[i];
}
#endif /* DHCP_REQLIST */
*opts++ = DHOP_END;
/* last_tick needs to be set in case we are doing a retry. It
* prevents dhc_second from calling us to do another retry while
* we are stuck
*/
dhc_states[iface].last_tick = cticks;
pkt->fhost = 0xFFFFFFFF; /* broadcast discovery request */
pkt->net = nets[iface]; /* send out caller spec'ed net */
/* we need to change the DHCP state before sending to avoid a
* race condition with the expected reply
*/
if (dhc_states[iface].state != DHCS_SELECTING)
dhc_set_state(iface, DHCS_SELECTING);
udp_send(BOOTP_SERVER_PORT, BOOTP_CLIENT_PORT, pkt);
dsc_discovers++;
/* state info is the same even if udp_send() failed */
dhc_states[iface].last_tick = cticks; /* set this again, post udp_send */
dhc_states[iface].tries++;
return 0;
}
/* FUNCTION: dhc_rx_offer()
*
* dhc_rx_offer() - process the received DHCPOFFER Process the
* received DHCP-OFFER and populate dhc_states[]. Then call
* dhc_request() to send a request packet.
*
*
* PARAM1: int iface
* PARAM2: struct bootp * bp
* PARAM3: unsigned bplen
*
* RETURNS: 0 if OK, else ENP_ error
*/
int
dhc_rx_offer(int iface, struct bootp * bp, unsigned bplen)
{
u_char * opts;
int e;
if (dhc_states[iface].xid != bp->xid)
return ENP_NOT_MINE;
opts = &bp->options[4]; /* examine options after cookie */
e = dhc_extract_opts(iface,opts);
if (e) /* parse error? */
{
dtrap("dhcpclnt 9\n");
return e;
}
if (!bp->yiaddr) /* require an IP address */
{
dhc_decline(iface,bp, bplen);
return ENP_NOT_MINE;
}
dhc_states[iface].ipaddr = bp->yiaddr;
/* if we got here, we must like the offer -- send a DHCP REQUEST */
return (dhc_request(iface,FALSE));
}
/* FUNCTION: dhc_request()
*
* dhc_request() - called when
* 1. we have received a DHCP offer (via dhc_rx_offer() ).
* 2. we have to renew a lease with a DHCP server. ( via dhc_reclaim() ).
* 3. we have to rebind to a different DHCP server.
* 4. we are in INIT-REBOOT state (via dhc_reclaim() ).
* This should format & send a request. Values are picked from
* dhc_states[]. It handles the specific nuances of a REQUEST
* depending on the state of DHCP Client. It implements the following
* table of RFC2131 (sec 4.3.6).
* ---------------------------------------------------------------------
* | |INIT-REBOOT |SELECTING |RENEWING |REBINDING |
* ---------------------------------------------------------------------
* |broad/unicast |broadcast |broadcast |unicast |broadcast |
* |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT |
* |requested-ip |MUST |MUST |MUST NOT |MUST NOT |
* |ciaddr |zero |zero |IP address |IP address|
* ---------------------------------------------------------------------
* In a DISCOVER->OFFER->REQUEST scenario, we need to use the same
* XID, SECS field as in OFFER packet. This is controlled via
* xid_flag. If xid_flag is TRUE, then use a new XID. Else use the
* old one.
*
* PARAM1: int iface
* PARAM2: int xid_flag
*
* RETURNS: Returns 0 if ok, else non-zero ENP_ error.
*/
int
dhc_request(int iface,int xid_flag)
{
struct bootp * outbp;
PACKET pkt; /* Outgoing packet */
u_char * opts; /* scratch pointer to DHCP options field */
ip_addr opt_ip; /* IP address temporary */
int e; /* error holder */
/* get a UDP packet buffer for sending DHCP request */
pkt = udp_alloc(sizeof(struct bootp) + DHCP_OPTSIZE - BOOTP_OPTSIZE, 0);
if (!pkt)
return ENP_NOMEM;
pkt->nb_plen = sizeof(struct bootp) - BOOTP_OPTSIZE;
if ( xid_flag == TRUE )
{
dhc_states[iface].xid = xids++;
dhc_states[iface].secs = 0;
}
/* build a BOOTP request header */
outbp = (struct bootp *)pkt->nb_prot;
e = dhc_buildheader(iface,outbp);
if (e)
return e;
/* turn it into a DHCP REQUEST packet */
*(long*)(&outbp->options) = RFC1084_MAGIC_COOKIE;
opts = &outbp->options[4]; /* encode options after cookie */
*opts++ = DHOP_TYPE;
*opts++ = 1; /* length of option field */
*opts++ = DHCP_REQUEST;
/* append the options that we want to request */
if ((dhc_states[iface].state == DHCS_SELECTING) ||
(dhc_states[iface].state == DHCS_REQUESTING) ||
(dhc_states[iface].state == DHCS_REBOOTING) ||
(dhc_states[iface].state == DHCS_INITREBOOT))
{
opt_ip = ntohl(dhc_states[iface].ipaddr);
PUT_IP_OPT(opts, DHOP_CADDR, opt_ip);
}
if (dhc_states[iface].snmask)
{
opt_ip = ntohl(dhc_states[iface].snmask);
PUT_IP_OPT(opts, DHOP_SNMASK, opt_ip);
}
if (dhc_states[iface].defgw)
{
opt_ip = ntohl(dhc_states[iface].defgw);
PUT_IP_OPT(opts, DHOP_ROUTER, opt_ip);
}
#if defined(DHC_MAXDNSRVS) && (DHC_MAXDNSRVS > 0)
{
int i;
u_char * optlen;
for (i = 0; i < DHC_MAXDNSRVS; i++)
{
if (dhc_states[iface].dnsrv[i])
{
if (i == 0)
{
*opts++ = DHOP_DNSRV;
optlen = opts;
*opts++ = 0;
}
/* Copy the IP address in nw byte order */
opt_ip = dhc_states[iface].dnsrv[i];
MEMCPY((char *)opts, (char *)&opt_ip,4);
opts+=4;
*optlen += 4;
}
}
}
#endif
if (dhc_states[iface].lease)
{
PUT_IP_OPT(opts, DHOP_LEASE, dhc_states[iface].lease);
}
/* If there is a list of options to be requested from server, include it*/
#ifdef DHCP_REQLIST
if ( reqlist_len > 0 )
{
int i;
*opts++ = DHOP_REQLIST ;
*opts++ = (u_char)reqlist_len ;
for (i=0 ; i < reqlist_len ; i++ )
*opts++ = reqlist[i];
}
#endif /* DHCP_REQLIST */
/* only set client IP address (ours) when renewing or rebinding */
if ((dhc_states[iface].state == DHCS_RENEWING)
|| (dhc_states[iface].state == DHCS_REBINDING))
{
outbp->ciaddr = nets[iface]->n_ipaddr;
}
/* RFC 2131, section 4.3.5 ref to server-ip is option 54 0x36, not
* siaddr: p. 16 number 3: "The client broadcasts a DHCPREQUEST
* message that MUST include the 'server identifier' option to
* indicate which server it has selected. . . ." p. 31 bullet 1:
* "Client inserts the address of the selected server in 'server
* identifier'. . . ." RFC 951, p. 4 definition of 'siaddr' is
* "server IP address; returned in bootreply by server."
*/
/* Only include server identifier option when selecting a server. */
if ((dhc_states[iface].state == DHCS_SELECTING) ||
(dhc_states[iface].state == DHCS_REQUESTING))
{
opt_ip = ntohl(dhc_states[iface].srv_ipaddr);
PUT_IP_OPT(opts, DHOP_SERVER, opt_ip);
}
#ifdef USE_AUTOIP
/* add hostname (code 12) */
PUT_STRING_OPT(opts, 12, dhc_hostname());
#endif /* USE_AUTOIP */
*opts++ = DHOP_END; /* Mark the end of options */
/* figure out whether to send via unicast or broadcast */
if (dhc_states[iface].state == DHCS_RENEWING)
{
pkt->fhost = dhc_states[iface].srv_ipaddr;
}
else
{
pkt->fhost = 0xFFFFFFFF; /* broadcast request */
}
pkt->net = nets[iface]; /* send out caller spec'ed net */
pkt->nb_plen = (char *)opts - (char *)outbp;
udp_send(BOOTP_SERVER_PORT, BOOTP_CLIENT_PORT, pkt);
dsc_requests++;
dhc_states[iface].last_tick = cticks;
dhc_states[iface].tries++;
return 0; /* return OK code */
}
#ifdef MINI_IP
/* FUNCTION: fixup_subnet_mask()
*
* This is a workalike to the function of the same name in the ip directory.
* If we are using MINI_IP then dhcp needs it's own copy of this.
*
* PARAM1: int netnum
*
* RETURNS:
*/
void
fixup_subnet_mask(int netnum) /* which of the nets[] to do. */
{
u_long smask;
if (m_netp->snmask) /* if mask is already set, don't bother */
return;
/* things depending on IP address class: */
if ((m_netp->n_ipaddr & AMASK) == AADDR)
smask = 0xFF000000L;
else if((m_netp->n_ipaddr & BMASK) == BADDR)
smask = 0xFFFF0000L;
else if((m_netp->n_ipaddr & CMASK) == CADDR)
smask = 0xFFFFFF00L;
else
{
dtrap("dhcpclnt 10\n"); /* bad logic or setup values */
smask = 0xFFFFFF00L;
}
m_netp->snmask = htonl(smask);
}
#endif /* MINI_IP */
/* FUNCTION: dhc_setip()
*
* dhc_setip() : Set the IP address (& other paras) for the interface
* It is called when we have received an ACK. That is, we have the IP
* address and other information needed for this interface. If the
* subnet mask has not been specified by the DHCP Server, then we
* will calculate one from the IP Address using fixup_subnet_mask()
*
* PARAM1: int iface
*
* RETURNS: Returns 0 if ok, else non-zero ENP error.
*/
int
dhc_setip(int iface)
{
nets[iface]->n_ipaddr = dhc_states[iface].ipaddr;
nets[iface]->snmask = dhc_states[iface].snmask;
nets[iface]->n_defgw = dhc_states[iface].defgw;
if ( nets[iface]->snmask == 0 )
{
fixup_subnet_mask(iface);
dhc_states[iface].snmask = nets[iface]->snmask;
}
/* fixup broadcast addresses */
nets[iface]->n_netbr = nets[iface]->n_ipaddr | ~nets[iface]->snmask;
nets[iface]->n_netbr42 = nets[iface]->n_ipaddr & nets[iface]->snmask;
nets[iface]->n_subnetbr = nets[iface]->n_ipaddr | ~nets[iface]->snmask;
return 0; /* return OK code */
}
/* FUNCTION: dhc_resetip()
*
* dhc_resetip() : Reset the IP address (& other paras) for the
* interface It is called when we didn't receive a ACK/NAK for the
* interface when RENEWING/REBINDING.
*
* PARAM1: int iface
*
* RETURNS: Returns 0 if ok, else non-zero error.
*/
int
dhc_resetip(int iface)
{
/* reset the ipaddress */
nets[iface]->n_ipaddr = 0;
nets[iface]->snmask = 0;
nets[iface]->n_defgw = 0;
/* reset the broadcast addresses */
nets[iface]->n_netbr = 0;
nets[iface]->n_netbr42 = 0;
nets[iface]->n_subnetbr = 0;
return 0; /* return OK code */
}
/* FUNCTION: dhc_decline()
*
* dhc_decline() - send a decline packet to server. This is usually
* because he didn't send us an IP address.
*
* PARAM1: int iface
* PARAM2: struct bootp * bp
* PARAM3: unsigned bplen
*
* RETURNS: Returns 0 if ok, else non-zero ENP_ error.
*/
int
dhc_decline(int iface,struct bootp * bp, unsigned bplen)
{
struct bootp * outbp;
PACKET pkt;
u_char * opts; /* scratch pointer to DHCP options field */
/* get a UDP packet buffer for sending DHCP */
pkt = udp_alloc(bplen, 0);
if (!pkt)
return ENP_NOMEM;
pkt->nb_plen = bplen;
outbp = (struct bootp *)pkt->nb_prot;
MEMCPY(outbp, bp, bplen);
outbp->op = BOOTREPLY;
/* find DHCP TYPE option so we can overwrite it */
opts = find_opt(DHOP_TYPE, &outbp->options[4]);
opts += 2; /* point to actual op code */
*opts = DHCP_DECLINE; /* overwrite op code */
pkt->fhost = 0xFFFFFFFF; /* broadcast decline pkt */
pkt->net = nets[iface]; /* send out caller speced net */
udp_send(BOOTP_SERVER_PORT, BOOTP_CLIENT_PORT, pkt);
dsc_declines++; /* count declines sent */
return 0;
}
/* FUNCTION: dh_getlong()
*
* dh_getlong() - routine to extract a 'long' from an arbitrary
* address NB: value is expected to remain in network byte order!
*
*
* PARAM1: u_char *ptr
*
* RETURNS: the extracted 32 bit value
*/
static long dh_getlong( u_char *ptr )
{
long v;
u_char * p2 = (u_char *)&v;
*p2++ = *ptr++;
*p2++ = *ptr++;
*p2++ = *ptr++;
*p2++ = *ptr++;
return v;
}
/* FUNCTION: dhc_extract_opts()
*
* dhc_extract_opts() - extract certain interesting options from the
* options string passed. These options are filled in to the fields
* passed.
*
* PARAM1: int iface
* PARAM2: u_char * opts
*
* RETURNS: Returns 0 if opts string was parsed OK, else non-zero if
* there was a parse error. 0 return doesn't mean all (or even any)
* of the options passed were filled in with good values.
*/
int
dhc_extract_opts(int iface,u_char * opts)
{
u_char * end = opts + DHCP_OPTSIZE; /* limit scope of search */
u_char optlen;
#if defined(DHC_MAXDNSRVS) && (DHC_MAXDNSRVS > 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -