📄 dhcpboot.c
字号:
/* dhcpboot.c:
* This code implements a subset of the DHCP client protocol.
* Based on RFC2131 spec, the "automatic allocation" mode, in which DHCP
* assigns a permanent IP address to a client, is the only mode supported.
*
* The idea is that the monitor boots up, and if IPADD is set to DHCP, then
* DHCP is used to populate shell variables with a server-supplied IP
* address, NetMask and Gateway IP address. Then, when the application
* is launched (probably via TFS), it can retrieve the content of those
* shell variables for use by the application.
*
* Sequence of events for this limited implementation of DHCP...
* Client issues a DHCP_DISCOVER, server responds with a DHCP_OFFER,
* client issues a DHCP_REQUEST and server responds with a DHCP_ACK.
* DISCOVER: request by the client to broadcast the fact that it is looking
* for a DHCP server.
* OFFER: reply from the server when it receives a DISCOVER request from
* a client. The offer may contain all the information that the DHCP
* client needs to bootup, but this is dependent on the configuration of
* the server.
* REQUEST: request by the client for the server (now known because an OFFER
* was received) to send it the information it needs.
* ACK: reply from the server with the information requested.
*
* NOTE: this file contains a generic DHCP client supporting "automatic
* allocation mode" (infinite lease time). There are several different
* application-specific enhancements that can be added and hopefully
* they have been isolated through the use of the dhcp_00.c file.
* I've attempted to isolate as much of the non-generic code to
* the file dhcp_XX.c (where dhcp_00.c is the default code). If non-default
* code is necessary, then limit the changes to a new dhcp_XX.c file. This
* will allow the code in this file to stay generic; hence, the user of this
* code will be able to accept monitor upgrades without the need to touch
* this file. The makefile must link in some additional dhcp_XX.c file
* (default is dhcp_00.c). Bottom line... there should be no need to modify
* this file for application-specific stuff; if there is, please let me know.
*
* NOTE1: the shell variable IPADD can also be set to DHCPV or DHCPv to
* enable different levels of verbosity during DHCP transactions... 'V'
* is full DHCP verbosity and 'v' only prints the DhcpSetEnv() calls.
*
* NOTE2: this file supports DHCP and BOOTP. Most of the function names
* refer to DHCP even though their functionality is shared by both DHCP
* and BOOTP. This is because I wrote this originally for DHCP, then added
* the hooks for BOOTP... Bottom line: don't let the names confuse you!
*
* General notice:
* This code is part of a boot-monitor package developed as a generic base
* platform for embedded system designs. As such, it is likely to be
* distributed to various projects beyond the control of the original
* author. Please notify the author of any enhancements made or bugs found
* so that all may benefit from the changes. In addition, notification back
* to the author will allow the new user to pick up changes that may have
* been made by other users after this version of the code was distributed.
*
* Note1: the majority of this code was edited with 4-space tabs.
* Note2: as more and more contributions are accepted, the term "author"
* is becoming a mis-representation of credit.
*
* Original author: Ed Sutter
* Email: esutter@lucent.com
* Phone: 908-582-2351
*/
#include "config.h"
#include "endian.h"
#include "cpuio.h"
#include "ether.h"
#include "tfs.h"
#include "tfsprivate.h"
#include "genlib.h"
#include "stddefs.h"
#include "cli.h"
#include "timer.h"
int DHCPStartup(short), BOOTPStartup(short);
int DhcpSetEnv(char *,char *);
int SendDHCPDiscover(int,short);
void dhcpDumpVsa(void), printDhcpOptions(uchar *);
unsigned short DHCPState;
#if INCLUDE_DHCPBOOT
static struct elapsed_tmr dhcpTmr;
static int DHCPCommandIssued;
static ulong DHCPTransactionId;
/* Variables used for DHCP Class ID specification: */
static char *DHCPClassId;
static int DHCPClassIdSize;
/* Variables used for DHCP Client ID specification: */
static char DHCPClientId[32];
static int DHCPClientIdSize, DHCPClientIdType;
/* Variables used for setting up a DHCP Parameter Request List: */
static uchar DHCPRequestList[32];
static int DHCPRqstListSize;
/* Variable to keep track of elapsed seconds since DHCP started: */
static short DHCPElapsedSecs;
char *DhcpHelp[] = {
"Issue a DHCP discover",
"-[brvV] [vsa]",
#if INCLUDE_VERBOSEHELP
"Options...",
" -b use bootp",
" -r retry",
" -v|V verbosity",
#endif
0,
};
int
Dhcp(int argc,char *argv[])
{
int opt, bootp;
bootp = 0;
DHCPCommandIssued = 1;
while ((opt=getopt(argc,argv,"brvV")) != -1) {
switch(opt) {
case 'b':
bootp = 1;
break;
case 'r':
DHCPCommandIssued = 0;
break;
case 'v':
EtherVerbose = SHOW_DHCP;
break;
case 'V':
EtherVerbose = DHCP_VERBOSE;
break;
default:
return(CMD_PARAM_ERROR);
}
}
if (argc == optind+1) {
if (!strcmp(argv[optind],"vsa")) {
dhcpDumpVsa();
return(CMD_SUCCESS);
}
else
return(CMD_PARAM_ERROR);
}
else if (argc != optind)
return(CMD_PARAM_ERROR);
startElapsedTimer(&dhcpTmr,RetransmitDelay(DELAY_INIT_DHCP)*1000);
if (bootp) {
DHCPState = BOOTPSTATE_INITIALIZE;
if (DHCPCommandIssued)
BOOTPStartup(0);
}
else {
DHCPState = DHCPSTATE_INITIALIZE;
if (DHCPCommandIssued)
DHCPStartup(0);
}
return(CMD_SUCCESS);
}
/* dhcpDumpVsa():
* Simply dump the content of the VSA shell variable in DHCP format.
* The variable content is stored in ascii and must be converted to binary
* prior to calling printDhcpOptions().
*/
void
dhcpDumpVsa(void)
{
int i;
char tmp[3], *vsa_b, *vsa_a, len;
vsa_a = getenv("DHCPVSA");
if ((!vsa_a) || (!strcmp(vsa_a,"TRUE")))
return;
len = strlen(vsa_a);
vsa_b = malloc(len);
if (!vsa_b)
return;
len >>= 1;
tmp[2] = 0;
for(i=0;i<len;i++) {
tmp[0] = *vsa_a++;
tmp[1] = *vsa_a++;
vsa_b[i] = (char)strtol(tmp,0,16);
}
/* First 4 bytes of DHCPVSA is the cookie, so skip over that. */
printDhcpOptions(vsa_b+4);
free(vsa_b);
}
void
dhcpDisable()
{
DHCPState = DHCPSTATE_NOTUSED;
}
/* DHCPStartup():
* This function is called at the point in which the ethernet interface is
* started if, and only if, the IPADD shell variable is set to DHCP.
* In older version of DHCP, the default was to use "LUCENT.PPA.1.1" as
* the default vcid. Now it is only used if specified in the shell variable
* DHCPCLASSID. The same strategy applies to DHCPCLIENTID.
*/
int
DHCPStartup(short seconds)
{
char *id, *colon, *rlist;
#if !INCLUDE_TFTP
printf("WARNING: DHCP can't load bootfile, TFTP not built into monitor.\n");
#endif
/* The format of DHCPCLASSID is simply a string of characters. */
id = getenv("DHCPCLASSID");
if (id)
DHCPClassId = id;
else
DHCPClassId = "";
DHCPClassIdSize = strlen(DHCPClassId);
/* The format of DHCPCLIENTID is "TYPE:ClientID" where 'TYPE is a
* decimal number ranging from 1-255 used as the "type" portion of
* the option, and ClientID is a string of ascii-coded hex pairs
* that are converted to binary and used as the client identifier.
*/
id = getenv("DHCPCLIENTID");
if (id) {
colon = strchr(id,':');
if ((colon) && (!(strlen(colon+1) & 1))) {
DHCPClientIdType = atoi(id);
colon++;
for(DHCPClientIdSize=0;*colon;DHCPClientIdSize++) {
uchar tmp;
tmp = colon[2];
colon[2] = 0;
DHCPClientId[DHCPClientIdSize] = (uchar)strtol(colon,0,16);
colon[2] = tmp;
colon+=2;
}
}
}
else
DHCPClientIdSize = 0;
/* The format of DHCPRQSTLIST is #:#:#:#:# where each '#' is a decimal
* number representing a parameter to be requested via the Parameter
* Request List option...
*/
rlist = getenv("DHCPRQSTLIST");
if (rlist) {
DHCPRqstListSize = 0;
colon = rlist;
while(*colon) {
if (*colon++ == ':')
DHCPRqstListSize++;
}
if (DHCPRqstListSize > sizeof(DHCPRequestList)) {
printf("DHCPRQSTLIST too big.\n");
DHCPRqstListSize = 0;
}
else {
char *rqst;
DHCPRqstListSize = 0;
rqst = rlist;
while(1) {
DHCPRequestList[DHCPRqstListSize++] = strtol(rqst,&colon,0);
if (*colon != ':')
break;
rqst = colon+1;
}
DHCPRequestList[DHCPRqstListSize] = 0;
}
}
else
DHCPRqstListSize = 0;
return(SendDHCPDiscover(0,seconds));
}
int
BOOTPStartup(short seconds)
{
return(SendDHCPDiscover(1,seconds));
}
uchar *
dhcpLoadShellVarOpts(uchar *options)
{
if (DHCPClassIdSize) {
*options++ = DHCPOPT_CLASSID;
*options++ = DHCPClassIdSize;
memcpy(options,DHCPClassId,DHCPClassIdSize);
options += DHCPClassIdSize;
}
if (DHCPClientIdSize) {
*options++ = DHCPOPT_CLIENTID;
*options++ = DHCPClientIdSize+1;
*options++ = DHCPClientIdType;
memcpy(options,DHCPClientId,DHCPClientIdSize);
options += DHCPClientIdSize;
}
if (DHCPRqstListSize) {
*options++ = DHCPOPT_PARMRQSTLIST;
*options++ = DHCPRqstListSize;
memcpy(options,DHCPRequestList,DHCPRqstListSize);
options += DHCPRqstListSize;
}
return(options);
}
/* SendDHCPDiscover()
* The DHCPDISCOVER is issued as an ethernet broadcast. IF the bootp
* flag is non-zero then just do a bootp request (a subset of the
* DHCPDISCOVER stuff).
*/
int
SendDHCPDiscover(int bootp,short seconds)
{
struct dhcphdr *dhcpdata;
struct bootphdr *bootpdata;
struct ether_header *te;
struct ip *ti;
struct Udphdr *tu;
ushort uh_ulen;
int optlen;
char *dhcpflags;
ulong cookie;
uchar *dhcpOptions, *dhcpOptionsBase;
/* Retrieve an ethernet buffer from the driver and populate the
* ethernet level of packet:
*/
te = (struct ether_header *) getXmitBuffer();
memcpy((char *)&te->ether_shost,BinEnetAddr,6);
memcpy((char *)&te->ether_dhost,BroadcastAddr,6);
te->ether_type = ecs(ETHERTYPE_IP);
/* Move to the IP portion of the packet and populate it appropriately: */
ti = (struct ip *) (te + 1);
ti->ip_vhl = IP_HDR_VER_LEN;
ti->ip_tos = 0;
ti->ip_id = 0;
ti->ip_off = ecs(0x4000); /* No fragmentation allowed */
ti->ip_ttl = UDP_TTL;
ti->ip_p = IP_UDP;
memset((char *)&ti->ip_src.s_addr,0,4);
memset((char *)&ti->ip_dst.s_addr,0xff,4);
/* Now udp... */
tu = (struct Udphdr *) (ti + 1);
tu->uh_sport = ecs(DhcpClientPort);
tu->uh_dport = ecs(DhcpServerPort);
/* First the stuff that is the same for BOOTP or DHCP... */
bootpdata = (struct bootphdr *)(tu+1);
dhcpdata = (struct dhcphdr *)(tu+1);
dhcpdata->op = DHCPBOOTP_REQUEST;
dhcpdata->htype = 1;
dhcpdata->hlen = 6;
dhcpdata->hops = 0;
dhcpdata->seconds = ecs(seconds);
memset(dhcpdata->bootfile,0,sizeof(dhcpdata->bootfile));
memset(dhcpdata->server_hostname,0,sizeof(dhcpdata->server_hostname));
/* For the first DHCPDISCOVER issued, establish a transaction id based
* on a crc32 of the mac address. For each DHCPDISCOVER after that,
* just increment.
*/
if (!DHCPTransactionId)
DHCPTransactionId = crc32(BinEnetAddr,6);
else
DHCPTransactionId++;
memcpy((char *)&dhcpdata->transaction_id,(char *)&DHCPTransactionId,4);
memset((char *)&dhcpdata->client_ip,0,4);
memset((char *)&dhcpdata->your_ip,0,4);
memset((char *)&dhcpdata->server_ip,0,4);
memset((char *)&dhcpdata->router_ip,0,4);
memcpy(dhcpdata->client_macaddr,BinEnetAddr,6);
dhcpflags = getenv("DHCPFLAGS");
if (dhcpflags) /* 0x8000 is the only bit used currently. */
dhcpdata->flags = (ushort)strtoul(dhcpflags,0,0);
else
dhcpdata->flags = 0;
self_ecs(dhcpdata->flags);
/* Finally, the DHCP or BOOTP specific stuff...
* Based on RFC1534 (Interoperation Between DHCP and BOOTP), any message
* received by a DHCP server that contains a 'DHCP_MESSAGETYPE' option
* is assumed to have been sent by a DHCP client. A message without the
* DHCP_MESSAGETYPE option is assumed to have been sent by a BOOTP
* client.
*/
uh_ulen = optlen = 0;
if (bootp) {
memset(bootpdata->vsa,0,sizeof(bootpdata->vsa));
uh_ulen = sizeof(struct Udphdr) + sizeof(struct bootphdr);
tu->uh_ulen = ecs(uh_ulen);
}
else {
if (!buildDhcpHdr(dhcpdata)) {
/* The cookie should only be loaded at the start of the
* vendor specific area if vendor-specific options are present.
*/
cookie = ecl(STANDARD_MAGIC_COOKIE);
memcpy((char *)&dhcpdata->magic_cookie,(char *)&cookie,4);
dhcpOptionsBase = (uchar *)(dhcpdata+1);
dhcpOptions = dhcpOptionsBase;
*dhcpOptions++ = DHCPOPT_MESSAGETYPE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -