bootp.c 22.7 KB
Newer Older
wdenk's avatar
wdenk committed
1 2 3 4 5 6 7
/*
 *	Based on LiMon - BOOTP.
 *
 *	Copyright 1994, 1995, 2000 Neil Russell.
 *	(See License)
 *	Copyright 2000 Roland Borde
 *	Copyright 2000 Paolo Scaffardi
8
 *	Copyright 2000-2004 Wolfgang Denk, wd@denx.de
wdenk's avatar
wdenk committed
9 10 11 12 13 14 15
 */

#include <common.h>
#include <command.h>
#include <net.h>
#include "bootp.h"
#include "tftp.h"
16
#include "nfs.h"
wdenk's avatar
wdenk committed
17 18 19
#ifdef CONFIG_STATUS_LED
#include <status_led.h>
#endif
20 21 22
#ifdef CONFIG_BOOTP_RANDOM_DELAY
#include "net_rand.h"
#endif
wdenk's avatar
wdenk committed
23

24
#define BOOTP_VENDOR_MAGIC	0x63825363	/* RFC1048 Magic Cookie */
wdenk's avatar
wdenk committed
25

26
#define TIMEOUT		5000UL	/* Milliseconds before trying BOOTP again */
27
#ifndef CONFIG_NET_RETRY_COUNT
28
# define TIMEOUT_COUNT	5		/* # of timeouts before giving up */
wdenk's avatar
wdenk committed
29
#else
30
# define TIMEOUT_COUNT	(CONFIG_NET_RETRY_COUNT)
wdenk's avatar
wdenk committed
31 32
#endif

33 34
#define PORT_BOOTPS	67		/* BOOTP server UDP port */
#define PORT_BOOTPC	68		/* BOOTP client UDP port */
wdenk's avatar
wdenk committed
35

36
#ifndef CONFIG_DHCP_MIN_EXT_LEN		/* minimal length of extension list */
37
#define CONFIG_DHCP_MIN_EXT_LEN 64
wdenk's avatar
wdenk committed
38 39 40 41 42
#endif

ulong		BootpID;
int		BootpTry;

43
#if defined(CONFIG_CMD_DHCP)
Kim Phillips's avatar
Kim Phillips committed
44 45 46
static dhcp_state_t dhcp_state = INIT;
static unsigned long dhcp_leasetime;
static IPaddr_t NetDHCPServerIP;
47 48
static void DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
			unsigned len);
wdenk's avatar
wdenk committed
49 50

/* For Debug */
51 52
#if 0
static char *dhcpmsg2str(int type)
wdenk's avatar
wdenk committed
53 54
{
	switch (type) {
55 56 57 58 59 60 61
	case 1:	 return "DHCPDISCOVER"; break;
	case 2:	 return "DHCPOFFER";	break;
	case 3:	 return "DHCPREQUEST";	break;
	case 4:	 return "DHCPDECLINE";	break;
	case 5:	 return "DHCPACK";	break;
	case 6:	 return "DHCPNACK";	break;
	case 7:	 return "DHCPRELEASE";	break;
wdenk's avatar
wdenk committed
62 63 64
	default: return "UNKNOWN/INVALID MSG TYPE"; break;
	}
}
65
#endif
66
#endif
wdenk's avatar
wdenk committed
67 68 69

static int BootpCheckPkt(uchar *pkt, unsigned dest, unsigned src, unsigned len)
{
70
	struct Bootp_t *bp = (struct Bootp_t *) pkt;
wdenk's avatar
wdenk committed
71 72 73 74
	int retval = 0;

	if (dest != PORT_BOOTPC || src != PORT_BOOTPS)
		retval = -1;
75
	else if (len < sizeof(struct Bootp_t) - OPT_FIELD_SIZE)
wdenk's avatar
wdenk committed
76 77
		retval = -2;
	else if (bp->bp_op != OP_BOOTREQUEST &&
78 79 80 81
			bp->bp_op != OP_BOOTREPLY &&
			bp->bp_op != DHCP_OFFER &&
			bp->bp_op != DHCP_ACK &&
			bp->bp_op != DHCP_NAK)
wdenk's avatar
wdenk committed
82 83 84 85 86
		retval = -3;
	else if (bp->bp_htype != HWT_ETHER)
		retval = -4;
	else if (bp->bp_hlen != HWL_ETHER)
		retval = -5;
87
	else if (NetReadLong((ulong *)&bp->bp_id) != BootpID)
wdenk's avatar
wdenk committed
88 89
		retval = -6;

Robin Getz's avatar
Robin Getz committed
90
	debug("Filtering pkt = %d\n", retval);
wdenk's avatar
wdenk committed
91 92 93 94 95 96 97

	return retval;
}

/*
 * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet
 */
98
static void BootpCopyNetParams(struct Bootp_t *bp)
wdenk's avatar
wdenk committed
99
{
100
#if !defined(CONFIG_BOOTP_SERVERIP)
101 102
	IPaddr_t tmp_ip;

103 104 105
	NetCopyIP(&tmp_ip, &bp->bp_siaddr);
	if (tmp_ip != 0)
		NetCopyIP(&NetServerIP, &bp->bp_siaddr);
106
	memcpy(NetServerEther, ((struct ethernet_hdr *)NetRxPacket)->et_src, 6);
107
#endif
108
	NetCopyIP(&NetOurIP, &bp->bp_yiaddr);
109
	if (strlen(bp->bp_file) > 0)
110
		copy_filename(BootFile, bp->bp_file, sizeof(BootFile));
wdenk's avatar
wdenk committed
111

Robin Getz's avatar
Robin Getz committed
112
	debug("Bootfile: %s\n", BootFile);
wdenk's avatar
wdenk committed
113 114

	/* Propagate to environment:
wdenk's avatar
wdenk committed
115
	 * don't delete exising entry when BOOTP / DHCP reply does
wdenk's avatar
wdenk committed
116 117
	 * not contain a new value
	 */
118 119
	if (*BootFile)
		setenv("bootfile", BootFile);
wdenk's avatar
wdenk committed
120 121
}

122
static int truncate_sz(const char *name, int maxlen, int curlen)
wdenk's avatar
wdenk committed
123 124
{
	if (curlen >= maxlen) {
125 126
		printf("*** WARNING: %s is too long (%d - max: %d)"
			" - truncated\n", name, curlen, maxlen);
wdenk's avatar
wdenk committed
127 128
		curlen = maxlen - 1;
	}
129
	return curlen;
wdenk's avatar
wdenk committed
130 131
}

132
#if !defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
133

134
static void BootpVendorFieldProcess(u8 *ext)
wdenk's avatar
wdenk committed
135
{
136
	int size = *(ext + 1);
wdenk's avatar
wdenk committed
137

Robin Getz's avatar
Robin Getz committed
138
	debug("[BOOTP] Processing extension %d... (%d bytes)\n", *ext,
139
		*(ext + 1));
wdenk's avatar
wdenk committed
140

141
	NetBootFileSize = 0;
wdenk's avatar
wdenk committed
142

143 144
	switch (*ext) {
		/* Fixed length fields */
145
	case 1:			/* Subnet mask */
wdenk's avatar
wdenk committed
146
		if (NetOurSubnetMask == 0)
147
			NetCopyIP(&NetOurSubnetMask, (IPaddr_t *) (ext + 2));
wdenk's avatar
wdenk committed
148
		break;
149
	case 2:			/* Time offset - Not yet supported */
wdenk's avatar
wdenk committed
150
		break;
151
		/* Variable length fields */
152 153 154
	case 3:			/* Gateways list */
		if (NetOurGatewayIP == 0)
			NetCopyIP(&NetOurGatewayIP, (IPaddr_t *) (ext + 2));
wdenk's avatar
wdenk committed
155
		break;
156
	case 4:			/* Time server - Not yet supported */
wdenk's avatar
wdenk committed
157
		break;
158
	case 5:			/* IEN-116 name server - Not yet supported */
wdenk's avatar
wdenk committed
159 160
		break;
	case 6:
161 162
		if (NetOurDNSIP == 0)
			NetCopyIP(&NetOurDNSIP, (IPaddr_t *) (ext + 2));
163
#if defined(CONFIG_BOOTP_DNS2)
164 165
		if ((NetOurDNS2IP == 0) && (size > 4))
			NetCopyIP(&NetOurDNS2IP, (IPaddr_t *) (ext + 2 + 4));
166
#endif
wdenk's avatar
wdenk committed
167
		break;
168
	case 7:			/* Log server - Not yet supported */
wdenk's avatar
wdenk committed
169
		break;
170
	case 8:			/* Cookie/Quote server - Not yet supported */
wdenk's avatar
wdenk committed
171
		break;
172
	case 9:			/* LPR server - Not yet supported */
wdenk's avatar
wdenk committed
173
		break;
174
	case 10:		/* Impress server - Not yet supported */
wdenk's avatar
wdenk committed
175
		break;
176
	case 11:		/* RPL server - Not yet supported */
wdenk's avatar
wdenk committed
177
		break;
178
	case 12:		/* Host name */
wdenk's avatar
wdenk committed
179
		if (NetOurHostName[0] == 0) {
180 181 182
			size = truncate_sz("Host Name",
				sizeof(NetOurHostName), size);
			memcpy(&NetOurHostName, ext + 2, size);
183
			NetOurHostName[size] = 0;
wdenk's avatar
wdenk committed
184 185
		}
		break;
186
	case 13:		/* Boot file size */
wdenk's avatar
wdenk committed
187
		if (size == 2)
188
			NetBootFileSize = ntohs(*(ushort *) (ext + 2));
wdenk's avatar
wdenk committed
189
		else if (size == 4)
190
			NetBootFileSize = ntohl(*(ulong *) (ext + 2));
wdenk's avatar
wdenk committed
191
		break;
192
	case 14:		/* Merit dump file - Not yet supported */
wdenk's avatar
wdenk committed
193
		break;
194
	case 15:		/* Domain name - Not yet supported */
wdenk's avatar
wdenk committed
195
		break;
196
	case 16:		/* Swap server - Not yet supported */
wdenk's avatar
wdenk committed
197
		break;
198
	case 17:		/* Root path */
wdenk's avatar
wdenk committed
199
		if (NetOurRootPath[0] == 0) {
200 201 202
			size = truncate_sz("Root Path",
				sizeof(NetOurRootPath), size);
			memcpy(&NetOurRootPath, ext + 2, size);
203
			NetOurRootPath[size] = 0;
wdenk's avatar
wdenk committed
204 205
		}
		break;
206
	case 18:		/* Extension path - Not yet supported */
wdenk's avatar
wdenk committed
207
		/*
wdenk's avatar
wdenk committed
208 209 210
		 * This can be used to send the information of the
		 * vendor area in another file that the client can
		 * access via TFTP.
wdenk's avatar
wdenk committed
211 212
		 */
		break;
213
		/* IP host layer fields */
214
	case 40:		/* NIS Domain name */
wdenk's avatar
wdenk committed
215
		if (NetOurNISDomain[0] == 0) {
216 217 218
			size = truncate_sz("NIS Domain Name",
				sizeof(NetOurNISDomain), size);
			memcpy(&NetOurNISDomain, ext + 2, size);
219
			NetOurNISDomain[size] = 0;
wdenk's avatar
wdenk committed
220 221
		}
		break;
222 223 224 225 226
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
	case 42:	/* NTP server IP */
		NetCopyIP(&NetNtpServerIP, (IPaddr_t *) (ext + 2));
		break;
#endif
227
		/* Application layer fields */
228
	case 43:		/* Vendor specific info - Not yet supported */
wdenk's avatar
wdenk committed
229
		/*
wdenk's avatar
wdenk committed
230 231
		 * Binary information to exchange specific
		 * product information.
wdenk's avatar
wdenk committed
232 233
		 */
		break;
234 235
		/* Reserved (custom) fields (128..254) */
	}
wdenk's avatar
wdenk committed
236 237
}

238
static void BootpVendorProcess(u8 *ext, int size)
wdenk's avatar
wdenk committed
239
{
240
	u8 *end = ext + size;
wdenk's avatar
wdenk committed
241

Robin Getz's avatar
Robin Getz committed
242
	debug("[BOOTP] Checking extension (%d bytes)...\n", size);
wdenk's avatar
wdenk committed
243

244 245 246 247 248 249 250 251
	while ((ext < end) && (*ext != 0xff)) {
		if (*ext == 0) {
			ext++;
		} else {
			u8 *opt = ext;

			ext += ext[1] + 2;
			if (ext <= end)
252
				BootpVendorFieldProcess(opt);
253
		}
wdenk's avatar
wdenk committed
254 255
	}

256
	debug("[BOOTP] Received fields:\n");
257
	if (NetOurSubnetMask)
Robin Getz's avatar
Robin Getz committed
258
		debug("NetOurSubnetMask : %pI4\n", &NetOurSubnetMask);
259

260
	if (NetOurGatewayIP)
Robin Getz's avatar
Robin Getz committed
261
		debug("NetOurGatewayIP	: %pI4", &NetOurGatewayIP);
262

Robin Getz's avatar
Robin Getz committed
263 264
	if (NetBootFileSize)
		debug("NetBootFileSize : %d\n", NetBootFileSize);
wdenk's avatar
wdenk committed
265

Robin Getz's avatar
Robin Getz committed
266 267
	if (NetOurHostName[0])
		debug("NetOurHostName  : %s\n", NetOurHostName);
268

Robin Getz's avatar
Robin Getz committed
269 270
	if (NetOurRootPath[0])
		debug("NetOurRootPath  : %s\n", NetOurRootPath);
271

Robin Getz's avatar
Robin Getz committed
272 273
	if (NetOurNISDomain[0])
		debug("NetOurNISDomain : %s\n", NetOurNISDomain);
274

Robin Getz's avatar
Robin Getz committed
275 276
	if (NetBootFileSize)
		debug("NetBootFileSize: %d\n", NetBootFileSize);
277 278 279 280 281

#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
	if (NetNtpServerIP)
		debug("NetNtpServerIP : %pI4\n", &NetNtpServerIP);
#endif
282
}
283

wdenk's avatar
wdenk committed
284 285 286 287
/*
 *	Handle a BOOTP received packet.
 */
static void
288 289
BootpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
	     unsigned len)
wdenk's avatar
wdenk committed
290
{
291
	struct Bootp_t *bp;
wdenk's avatar
wdenk committed
292

Robin Getz's avatar
Robin Getz committed
293
	debug("got BOOTP packet (src=%d, dst=%d, len=%d want_len=%zu)\n",
294
		src, dest, len, sizeof(struct Bootp_t));
wdenk's avatar
wdenk committed
295

296
	bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
297

298 299
	/* Filter out pkts we don't want */
	if (BootpCheckPkt(pkt, dest, src, len))
wdenk's avatar
wdenk committed
300 301 302
		return;

	/*
303
	 *	Got a good BOOTP reply.	 Copy the data into our variables.
wdenk's avatar
wdenk committed
304 305
	 */
#ifdef CONFIG_STATUS_LED
306
	status_led_set(STATUS_LED_BOOT, STATUS_LED_OFF);
wdenk's avatar
wdenk committed
307 308 309 310 311
#endif

	BootpCopyNetParams(bp);		/* Store net parameters from reply */

	/* Retrieve extended information (we must parse the vendor area) */
312
	if (NetReadLong((ulong *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC))
Wolfgang Denk's avatar
Wolfgang Denk committed
313
		BootpVendorProcess((uchar *)&bp->bp_vend[4], len);
wdenk's avatar
wdenk committed
314 315

	NetSetTimeout(0, (thand_f *)0);
316
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
wdenk's avatar
wdenk committed
317

Robin Getz's avatar
Robin Getz committed
318
	debug("Got good BOOTP\n");
wdenk's avatar
wdenk committed
319

320
	net_auto_load();
wdenk's avatar
wdenk committed
321
}
322
#endif
wdenk's avatar
wdenk committed
323 324 325 326 327 328 329 330

/*
 *	Timeout on BOOTP/DHCP request.
 */
static void
BootpTimeout(void)
{
	if (BootpTry >= TIMEOUT_COUNT) {
331 332
#ifdef CONFIG_BOOTP_MAY_FAIL
		puts("\nRetry count exceeded\n");
333
		net_set_state(NETLOOP_FAIL);
334
#else
335 336
		puts("\nRetry count exceeded; starting again\n");
		NetStartAgain();
337
#endif
wdenk's avatar
wdenk committed
338
	} else {
339 340
		NetSetTimeout(TIMEOUT, BootpTimeout);
		BootpRequest();
wdenk's avatar
wdenk committed
341 342 343
	}
}

344 345 346 347 348 349 350 351 352
#define put_vci(e, str)						\
	do {							\
		size_t vci_strlen = strlen(str);		\
		*e++ = 60;	/* Vendor Class Identifier */	\
		*e++ = vci_strlen;				\
		memcpy(e, str, vci_strlen);			\
		e += vci_strlen;				\
	} while (0)

wdenk's avatar
wdenk committed
353 354 355
/*
 *	Initialize BOOTP extension fields in the request.
 */
356
#if defined(CONFIG_CMD_DHCP)
357 358
static int DhcpExtended(u8 *e, int message_type, IPaddr_t ServerID,
			IPaddr_t RequestedIP)
wdenk's avatar
wdenk committed
359
{
360 361
	u8 *start = e;
	u8 *cnt;
362 363 364 365
#if defined(CONFIG_BOOTP_PXE)
	char *uuid;
	u16 clientarch;
#endif
366

367
#if defined(CONFIG_BOOTP_VENDOREX)
368
	u8 *x;
wdenk's avatar
wdenk committed
369
#endif
370
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
Wolfgang Denk's avatar
Wolfgang Denk committed
371
	char *hostname;
372
#endif
wdenk's avatar
wdenk committed
373

374 375 376 377
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
378

379 380 381
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = message_type;
wdenk's avatar
wdenk committed
382

383 384
	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
385 386
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
wdenk's avatar
wdenk committed
387

388
	if (ServerID) {
389
		int tmp = ntohl(ServerID);
wdenk's avatar
wdenk committed
390

391 392 393 394 395 396 397
		*e++ = 54;	/* ServerID */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
wdenk's avatar
wdenk committed
398

399
	if (RequestedIP) {
400
		int tmp = ntohl(RequestedIP);
wdenk's avatar
wdenk committed
401

402 403 404 405 406 407 408
		*e++ = 50;	/* Requested IP */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
409
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
410 411 412
	hostname = getenv("hostname");
	if (hostname) {
		int hostnamelen = strlen(hostname);
413 414 415

		*e++ = 12;	/* Hostname */
		*e++ = hostnamelen;
416
		memcpy(e, hostname, hostnamelen);
417 418
		e += hostnamelen;
	}
419 420
#endif

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
#if defined(CONFIG_BOOTP_PXE)
	clientarch = CONFIG_BOOTP_PXE_CLIENTARCH;
	*e++ = 93;	/* Client System Architecture */
	*e++ = 2;
	*e++ = (clientarch >> 8) & 0xff;
	*e++ = clientarch & 0xff;

	*e++ = 94;	/* Client Network Interface Identifier */
	*e++ = 3;
	*e++ = 1;	/* type field for UNDI */
	*e++ = 0;	/* major revision */
	*e++ = 0;	/* minor revision */

	uuid = getenv("pxeuuid");

	if (uuid) {
		if (uuid_str_valid(uuid)) {
			*e++ = 97;	/* Client Machine Identifier */
			*e++ = 17;
			*e++ = 0;	/* type 0 - UUID */

442
			uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD);
443 444 445 446 447
			e += 16;
		} else {
			printf("Invalid pxeuuid: %s\n", uuid);
		}
	}
448
#endif
449

450 451
#ifdef CONFIG_BOOTP_VCI_STRING
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
452 453
#endif

454
#if defined(CONFIG_BOOTP_VENDOREX)
455 456
	x = dhcp_vendorex_prep(e);
	if (x)
457
		return x - start;
wdenk's avatar
wdenk committed
458 459
#endif

460 461 462
	*e++ = 55;		/* Parameter Request List */
	 cnt = e++;		/* Pointer to count of requested items */
	*cnt = 0;
463
#if defined(CONFIG_BOOTP_SUBNETMASK)
464 465
	*e++  = 1;		/* Subnet Mask */
	*cnt += 1;
wdenk's avatar
wdenk committed
466
#endif
467
#if defined(CONFIG_BOOTP_TIMEOFFSET)
468 469 470
	*e++  = 2;
	*cnt += 1;
#endif
471
#if defined(CONFIG_BOOTP_GATEWAY)
472 473
	*e++  = 3;		/* Router Option */
	*cnt += 1;
wdenk's avatar
wdenk committed
474
#endif
475
#if defined(CONFIG_BOOTP_DNS)
476 477
	*e++  = 6;		/* DNS Server(s) */
	*cnt += 1;
wdenk's avatar
wdenk committed
478
#endif
479
#if defined(CONFIG_BOOTP_HOSTNAME)
480 481
	*e++  = 12;		/* Hostname */
	*cnt += 1;
wdenk's avatar
wdenk committed
482
#endif
483
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
484 485
	*e++  = 13;		/* Boot File Size */
	*cnt += 1;
wdenk's avatar
wdenk committed
486
#endif
487
#if defined(CONFIG_BOOTP_BOOTPATH)
488 489
	*e++  = 17;		/* Boot path */
	*cnt += 1;
wdenk's avatar
wdenk committed
490
#endif
491
#if defined(CONFIG_BOOTP_NISDOMAIN)
492 493
	*e++  = 40;		/* NIS Domain name request */
	*cnt += 1;
494
#endif
495
#if defined(CONFIG_BOOTP_NTPSERVER)
496 497
	*e++  = 42;
	*cnt += 1;
wdenk's avatar
wdenk committed
498
#endif
499 500 501 502
	/* no options, so back up to avoid sending an empty request list */
	if (*cnt == 0)
		e -= 2;

503
	*e++  = 255;		/* End of the list */
wdenk's avatar
wdenk committed
504

505
	/* Pad to minimal length */
wdenk's avatar
wdenk committed
506
#ifdef	CONFIG_DHCP_MIN_EXT_LEN
507
	while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN)
508
		*e++ = 0;
wdenk's avatar
wdenk committed
509 510
#endif

511
	return e - start;
wdenk's avatar
wdenk committed
512 513
}

514
#else
wdenk's avatar
wdenk committed
515
/*
516
 * Warning: no field size check - change CONFIG_BOOTP_* at your own risk!
wdenk's avatar
wdenk committed
517
 */
518
static int BootpExtended(u8 *e)
wdenk's avatar
wdenk committed
519
{
520
	u8 *start = e;
wdenk's avatar
wdenk committed
521

522 523 524 525
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
526

527
#if defined(CONFIG_CMD_DHCP)
528 529 530 531 532 533
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = DHCP_DISCOVER;

	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
534 535
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
536
#endif
wdenk's avatar
wdenk committed
537

538 539 540 541 542
#if defined(CONFIG_BOOTP_VCI_STRING) || \
	(defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING))
#ifdef CONFIG_SPL_BUILD
	put_vci(e, CONFIG_SPL_NET_VCI_STRING);
#else
543 544
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
#endif
545
#endif
546

547
#if defined(CONFIG_BOOTP_SUBNETMASK)
548 549 550
	*e++ = 1;		/* Subnet mask request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
551 552
#endif

553
#if defined(CONFIG_BOOTP_GATEWAY)
554 555 556
	*e++ = 3;		/* Default gateway request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
557 558
#endif

559
#if defined(CONFIG_BOOTP_DNS)
560 561 562
	*e++ = 6;		/* Domain Name Server */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
563 564
#endif

565
#if defined(CONFIG_BOOTP_HOSTNAME)
566 567 568
	*e++ = 12;		/* Host name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
569 570
#endif

571
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
572 573 574
	*e++ = 13;		/* Boot file size */
	*e++ = 2;
	e   += 2;
wdenk's avatar
wdenk committed
575 576
#endif

577
#if defined(CONFIG_BOOTP_BOOTPATH)
578 579 580
	*e++ = 17;		/* Boot path */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
581 582
#endif

583
#if defined(CONFIG_BOOTP_NISDOMAIN)
584 585 586
	*e++ = 40;		/* NIS Domain name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
587
#endif
588 589 590 591 592
#if defined(CONFIG_BOOTP_NTPSERVER)
	*e++ = 42;
	*e++ = 4;
	e   += 4;
#endif
wdenk's avatar
wdenk committed
593

594
	*e++ = 255;		/* End of the list */
wdenk's avatar
wdenk committed
595

596
	return e - start;
wdenk's avatar
wdenk committed
597
}
598
#endif
wdenk's avatar
wdenk committed
599 600

void
601
BootpRequest(void)
wdenk's avatar
wdenk committed
602
{
603
	uchar *pkt, *iphdr;
604
	struct Bootp_t *bp;
605 606
	int extlen, pktlen, iplen;
	int eth_hdr_size;
607
#ifdef CONFIG_BOOTP_RANDOM_DELAY
Pavel Machek's avatar
Pavel Machek committed
608
	ulong rand_ms;
609
#endif
wdenk's avatar
wdenk committed
610

611
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
612
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
613 614 615 616
	dhcp_state = INIT;
#endif

#ifdef CONFIG_BOOTP_RANDOM_DELAY		/* Random BOOTP delay */
617 618
	if (BootpTry == 0)
		srand_mac();
wdenk's avatar
wdenk committed
619

620 621 622 623
	if (BootpTry <= 2)	/* Start with max 1024 * 1ms */
		rand_ms = rand() >> (22 - BootpTry);
	else		/* After 3rd BOOTP request max 8192 * 1ms */
		rand_ms = rand() >> 19;
wdenk's avatar
wdenk committed
624

625
	printf("Random delay: %ld ms...\n", rand_ms);
Pavel Machek's avatar
Pavel Machek committed
626
	mdelay(rand_ms);
627

wdenk's avatar
wdenk committed
628 629 630 631
#endif	/* CONFIG_BOOTP_RANDOM_DELAY */

	printf("BOOTP broadcast %d\n", ++BootpTry);
	pkt = NetTxPacket;
632
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
633

634 635
	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_IP);
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
636 637

	/*
638 639 640 641
	 * Next line results in incorrect packet size being transmitted,
	 * resulting in errors in some DHCP servers, reporting missing bytes.
	 * Size must be set in packet header after extension length has been
	 * determined.
wdenk's avatar
wdenk committed
642 643
	 * C. Hallinan, DS4.COM, Inc.
	 */
644
	/* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC,
645
		sizeof (struct Bootp_t)); */
646
	iphdr = pkt;	/* We need this later for net_set_udp_header() */
647
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
648

649
	bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
650 651 652 653
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
654
	bp->bp_secs = htons(get_timer(0) / 1000);
wdenk's avatar
wdenk committed
655 656 657 658
	NetWriteIP(&bp->bp_ciaddr, 0);
	NetWriteIP(&bp->bp_yiaddr, 0);
	NetWriteIP(&bp->bp_siaddr, 0);
	NetWriteIP(&bp->bp_giaddr, 0);
659 660
	memcpy(bp->bp_chaddr, NetOurEther, 6);
	copy_filename(bp->bp_file, BootFile, sizeof(bp->bp_file));
wdenk's avatar
wdenk committed
661 662

	/* Request additional information from the BOOTP/DHCP server */
663
#if defined(CONFIG_CMD_DHCP)
664
	extlen = DhcpExtended((u8 *)bp->bp_vend, DHCP_DISCOVER, 0, 0);
wdenk's avatar
wdenk committed
665
#else
666
	extlen = BootpExtended((u8 *)bp->bp_vend);
667
#endif
wdenk's avatar
wdenk committed
668 669 670

	/*
	 *	Bootp ID is the lower 4 bytes of our ethernet address
671
	 *	plus the current time in ms.
wdenk's avatar
wdenk committed
672 673 674 675 676 677
	 */
	BootpID = ((ulong)NetOurEther[2] << 24)
		| ((ulong)NetOurEther[3] << 16)
		| ((ulong)NetOurEther[4] << 8)
		| (ulong)NetOurEther[5];
	BootpID += get_timer(0);
678
	BootpID	 = htonl(BootpID);
wdenk's avatar
wdenk committed
679 680 681 682 683 684
	NetCopyLong(&bp->bp_id, &BootpID);

	/*
	 * Calculate proper packet lengths taking into account the
	 * variable size of the options field
	 */
685 686
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
687
	net_set_udp_header(iphdr, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, iplen);
688
	NetSetTimeout(SELECT_TIMEOUT, BootpTimeout);
wdenk's avatar
wdenk committed
689

690
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
691
	dhcp_state = SELECTING;
692
	net_set_udp_handler(DhcpHandler);
wdenk's avatar
wdenk committed
693
#else
694
	net_set_udp_handler(BootpHandler);
695
#endif
wdenk's avatar
wdenk committed
696 697 698
	NetSendPacket(NetTxPacket, pktlen);
}

699
#if defined(CONFIG_CMD_DHCP)
700
static void DhcpOptionsProcess(uchar *popt, struct Bootp_t *bp)
wdenk's avatar
wdenk committed
701
{
702
	uchar *end = popt + BOOTP_HDR_SIZE;
wdenk's avatar
wdenk committed
703
	int oplen, size;
704 705 706
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
	int *to_ptr;
#endif
wdenk's avatar
wdenk committed
707

708
	while (popt < end && *popt != 0xff) {
wdenk's avatar
wdenk committed
709
		oplen = *(popt + 1);
710 711
		switch (*popt) {
		case 1:
712
			NetCopyIP(&NetOurSubnetMask, (popt + 2));
713
			break;
714
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
715
		case 2:		/* Time offset	*/
716
			to_ptr = &NetTimeOffset;
717 718
			NetCopyLong((ulong *)to_ptr, (ulong *)(popt + 2));
			NetTimeOffset = ntohl(NetTimeOffset);
719 720
			break;
#endif
721
		case 3:
722
			NetCopyIP(&NetOurGatewayIP, (popt + 2));
723 724
			break;
		case 6:
725
			NetCopyIP(&NetOurDNSIP, (popt + 2));
726
#if defined(CONFIG_BOOTP_DNS2)
727 728
			if (*(popt + 1) > 4)
				NetCopyIP(&NetOurDNS2IP, (popt + 2 + 4));
729
#endif
730 731
			break;
		case 12:
732 733 734
			size = truncate_sz("Host Name",
				sizeof(NetOurHostName), oplen);
			memcpy(&NetOurHostName, popt + 2, size);
735 736 737 738 739
			NetOurHostName[size] = 0;
			break;
		case 15:	/* Ignore Domain Name Option */
			break;
		case 17:
740 741 742
			size = truncate_sz("Root Path",
				sizeof(NetOurRootPath), oplen);
			memcpy(&NetOurRootPath, popt + 2, size);
743 744
			NetOurRootPath[size] = 0;
			break;
745 746
		case 28:	/* Ignore Broadcast Address Option */
			break;
747
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
748
		case 42:	/* NTP server IP */
749
			NetCopyIP(&NetNtpServerIP, (popt + 2));
750 751
			break;
#endif
752
		case 51:
753
			NetCopyLong(&dhcp_leasetime, (ulong *) (popt + 2));
754 755 756 757
			break;
		case 53:	/* Ignore Message Type Option */
			break;
		case 54:
758
			NetCopyIP(&NetDHCPServerIP, (popt + 2));
759 760 761 762 763
			break;
		case 58:	/* Ignore Renewal Time Option */
			break;
		case 59:	/* Ignore Rebinding Time Option */
			break;
764 765 766 767 768 769 770 771 772
		case 66:	/* Ignore TFTP server name */
			break;
		case 67:	/* vendor opt bootfile */
			/*
			 * I can't use dhcp_vendorex_proc here because I need
			 * to write into the bootp packet - even then I had to
			 * pass the bootp packet pointer into here as the
			 * second arg
			 */
773
			size = truncate_sz("Opt Boot File",
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
					    sizeof(bp->bp_file),
					    oplen);
			if (bp->bp_file[0] == '\0' && size > 0) {
				/*
				 * only use vendor boot file if we didn't
				 * receive a boot file in the main non-vendor
				 * part of the packet - god only knows why
				 * some vendors chose not to use this perfectly
				 * good spot to store the boot file (join on
				 * Tru64 Unix) it seems mind bogglingly crazy
				 * to me
				 */
				printf("*** WARNING: using vendor "
					"optional boot file\n");
				memcpy(bp->bp_file, popt + 2, size);
				bp->bp_file[size] = '\0';
			}
			break;
792
		default:
793
#if defined(CONFIG_BOOTP_VENDOREX)
794
			if (dhcp_vendorex_proc(popt))
wdenk's avatar
wdenk committed
795
				break;
wdenk's avatar
wdenk committed
796
#endif
797 798
			printf("*** Unhandled DHCP Option in OFFER/ACK:"
				" %d\n", *popt);
799
			break;
wdenk's avatar
wdenk committed
800 801 802 803 804 805 806
		}
		popt += oplen + 2;	/* Process next option */
	}
}

static int DhcpMessageType(unsigned char *popt)
{
807
	if (NetReadLong((ulong *)popt) != htonl(BOOTP_VENDOR_MAGIC))
wdenk's avatar
wdenk committed
808 809 810
		return -1;

	popt += 4;
811 812
	while (*popt != 0xff) {
		if (*popt == 53)	/* DHCP Message Type */
wdenk's avatar
wdenk committed
813 814 815 816 817 818
			return *(popt + 2);
		popt += *(popt + 1) + 2;	/* Scan through all options */
	}
	return -1;
}

819
static void DhcpSendRequestPkt(struct Bootp_t *bp_offer)
wdenk's avatar
wdenk committed
820
{
821
	uchar *pkt, *iphdr;
822
	struct Bootp_t *bp;
wdenk's avatar
wdenk committed
823
	int pktlen, iplen, extlen;
824
	int eth_hdr_size;
825
	IPaddr_t OfferedIP;
wdenk's avatar
wdenk committed
826

Robin Getz's avatar
Robin Getz committed
827
	debug("DhcpSendRequestPkt: Sending DHCPREQUEST\n");
wdenk's avatar
wdenk committed
828
	pkt = NetTxPacket;
829
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
830

831 832
	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_IP);
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
833

834
	iphdr = pkt;	/* We'll need this later to set proper pkt size */
835
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
836

837
	bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
838 839 840 841
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
842
	bp->bp_secs = htons(get_timer(0) / 1000);
843 844
	/* Do not set the client IP, your IP, or server IP yet, since it
	 * hasn't been ACK'ed by the server yet */
Justin Flammia's avatar
Justin Flammia committed
845

Wolfgang Denk's avatar
Wolfgang Denk committed
846
	/*
847 848 849 850 851
	 * RFC3046 requires Relay Agents to discard packets with
	 * nonzero and offered giaddr
	 */
	NetWriteIP(&bp->bp_giaddr, 0);

852
	memcpy(bp->bp_chaddr, NetOurEther, 6);
wdenk's avatar
wdenk committed
853 854 855 856 857 858 859 860 861 862

	/*
	 * ID is the id of the OFFER packet
	 */

	NetCopyLong(&bp->bp_id, &bp_offer->bp_id);

	/*
	 * Copy options from OFFER packet if present
	 */
Justin Flammia's avatar
Justin Flammia committed
863 864 865

	/* Copy offered IP into the parameters request list */
	NetCopyIP(&OfferedIP, &bp_offer->bp_yiaddr);
866 867
	extlen = DhcpExtended((u8 *)bp->bp_vend, DHCP_REQUEST,
		NetDHCPServerIP, OfferedIP);
wdenk's avatar
wdenk committed
868

869 870
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
871
	net_set_udp_header(iphdr, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, iplen);
wdenk's avatar
wdenk committed
872

873 874 875
#ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY
	udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY);
#endif	/* CONFIG_BOOTP_DHCP_REQUEST_DELAY */
876
	debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen);
wdenk's avatar
wdenk committed
877 878 879 880 881 882 883
	NetSendPacket(NetTxPacket, pktlen);
}

/*
 *	Handle DHCP received packets.
 */
static void
884 885
DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
	    unsigned len)
wdenk's avatar
wdenk committed
886
{
887
	struct Bootp_t *bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
888

Robin Getz's avatar
Robin Getz committed
889
	debug("DHCPHandler: got packet: (src=%d, dst=%d, len=%d) state: %d\n",
wdenk's avatar
wdenk committed
890 891
		src, dest, len, dhcp_state);

892 893
	/* Filter out pkts we don't want */
	if (BootpCheckPkt(pkt, dest, src, len))
wdenk's avatar
wdenk committed
894 895
		return;

896 897
	debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state:"
		" %d\n", src, dest, len, dhcp_state);
wdenk's avatar
wdenk committed
898 899 900 901 902

	switch (dhcp_state) {
	case SELECTING:
		/*
		 * Wait an appropriate time for any potential DHCPOFFER packets
903 904 905
		 * to arrive.  Then select one, and generate DHCPREQUEST
		 * response.  If filename is in format we recognize, assume it
		 * is a valid OFFER from a server we want.
wdenk's avatar
wdenk committed
906
		 */
Robin Getz's avatar
Robin Getz committed
907
		debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file);
908
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
909
		if (strncmp(bp->bp_file,
910
			    CONFIG_SYS_BOOTFILE_PREFIX,
911
			    strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
912
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
wdenk's avatar
wdenk committed
913

Robin Getz's avatar
Robin Getz committed
914
			debug("TRANSITIONING TO REQUESTING STATE\n");
wdenk's avatar
wdenk committed
915
			dhcp_state = REQUESTING;
916

917 918
			if (NetReadLong((ulong *)&bp->bp_vend[0]) ==
						htonl(BOOTP_VENDOR_MAGIC))
919
				DhcpOptionsProcess((u8 *)&bp->bp_vend[4], bp);
wdenk's avatar
wdenk committed
920

921
			NetSetTimeout(TIMEOUT, BootpTimeout);
wdenk's avatar
wdenk committed
922
			DhcpSendRequestPkt(bp);
923
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
924
		}
925
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
wdenk's avatar
wdenk committed
926 927 928 929

		return;
		break;
	case REQUESTING:
Robin Getz's avatar
Robin Getz committed