bootp.c 23.3 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 27 28 29 30 31 32 33 34 35
/*
 * The timeout for the initial BOOTP/DHCP request used to be described by a
 * counter of fixed-length timeout periods. TIMEOUT_COUNT represents
 * that counter
 *
 * Now that the timeout periods are variable (exponential backoff and retry)
 * we convert the timeout count to the absolute time it would have take to
 * execute that many retries, and keep sending retry packets until that time
 * is reached.
 */
36
#ifndef CONFIG_NET_RETRY_COUNT
37
# define TIMEOUT_COUNT	5		/* # of timeouts before giving up */
wdenk's avatar
wdenk committed
38
#else
39
# define TIMEOUT_COUNT	(CONFIG_NET_RETRY_COUNT)
wdenk's avatar
wdenk committed
40
#endif
41
#define TIMEOUT_MS	((3 + (TIMEOUT_COUNT * 5)) * 1000)
wdenk's avatar
wdenk committed
42

43 44
#define PORT_BOOTPS	67		/* BOOTP server UDP port */
#define PORT_BOOTPC	68		/* BOOTP client UDP port */
wdenk's avatar
wdenk committed
45

46
#ifndef CONFIG_DHCP_MIN_EXT_LEN		/* minimal length of extension list */
47
#define CONFIG_DHCP_MIN_EXT_LEN 64
wdenk's avatar
wdenk committed
48 49 50 51
#endif

ulong		BootpID;
int		BootpTry;
52 53
ulong		bootp_start;
ulong		bootp_timeout;
wdenk's avatar
wdenk committed
54

55
#if defined(CONFIG_CMD_DHCP)
Kim Phillips's avatar
Kim Phillips committed
56 57 58
static dhcp_state_t dhcp_state = INIT;
static unsigned long dhcp_leasetime;
static IPaddr_t NetDHCPServerIP;
59 60
static void DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
			unsigned len);
wdenk's avatar
wdenk committed
61 62

/* For Debug */
63 64
#if 0
static char *dhcpmsg2str(int type)
wdenk's avatar
wdenk committed
65 66
{
	switch (type) {
67 68 69 70 71 72 73
	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
74 75 76
	default: return "UNKNOWN/INVALID MSG TYPE"; break;
	}
}
77
#endif
78
#endif
wdenk's avatar
wdenk committed
79 80 81

static int BootpCheckPkt(uchar *pkt, unsigned dest, unsigned src, unsigned len)
{
82
	struct Bootp_t *bp = (struct Bootp_t *) pkt;
wdenk's avatar
wdenk committed
83 84 85 86
	int retval = 0;

	if (dest != PORT_BOOTPC || src != PORT_BOOTPS)
		retval = -1;
87
	else if (len < sizeof(struct Bootp_t) - OPT_FIELD_SIZE)
wdenk's avatar
wdenk committed
88 89
		retval = -2;
	else if (bp->bp_op != OP_BOOTREQUEST &&
90 91 92 93
			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
94 95 96 97 98
		retval = -3;
	else if (bp->bp_htype != HWT_ETHER)
		retval = -4;
	else if (bp->bp_hlen != HWL_ETHER)
		retval = -5;
99
	else if (NetReadLong((ulong *)&bp->bp_id) != BootpID)
wdenk's avatar
wdenk committed
100 101
		retval = -6;

Robin Getz's avatar
Robin Getz committed
102
	debug("Filtering pkt = %d\n", retval);
wdenk's avatar
wdenk committed
103 104 105 106 107 108 109

	return retval;
}

/*
 * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet
 */
110
static void BootpCopyNetParams(struct Bootp_t *bp)
wdenk's avatar
wdenk committed
111
{
112
#if !defined(CONFIG_BOOTP_SERVERIP)
113 114
	IPaddr_t tmp_ip;

115 116 117
	NetCopyIP(&tmp_ip, &bp->bp_siaddr);
	if (tmp_ip != 0)
		NetCopyIP(&NetServerIP, &bp->bp_siaddr);
118
	memcpy(NetServerEther, ((struct ethernet_hdr *)NetRxPacket)->et_src, 6);
119
#endif
120
	NetCopyIP(&NetOurIP, &bp->bp_yiaddr);
121
	if (strlen(bp->bp_file) > 0)
122
		copy_filename(BootFile, bp->bp_file, sizeof(BootFile));
wdenk's avatar
wdenk committed
123

Robin Getz's avatar
Robin Getz committed
124
	debug("Bootfile: %s\n", BootFile);
wdenk's avatar
wdenk committed
125 126

	/* Propagate to environment:
wdenk's avatar
wdenk committed
127
	 * don't delete exising entry when BOOTP / DHCP reply does
wdenk's avatar
wdenk committed
128 129
	 * not contain a new value
	 */
130 131
	if (*BootFile)
		setenv("bootfile", BootFile);
wdenk's avatar
wdenk committed
132 133
}

134
static int truncate_sz(const char *name, int maxlen, int curlen)
wdenk's avatar
wdenk committed
135 136
{
	if (curlen >= maxlen) {
137 138
		printf("*** WARNING: %s is too long (%d - max: %d)"
			" - truncated\n", name, curlen, maxlen);
wdenk's avatar
wdenk committed
139 140
		curlen = maxlen - 1;
	}
141
	return curlen;
wdenk's avatar
wdenk committed
142 143
}

144
#if !defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
145

146
static void BootpVendorFieldProcess(u8 *ext)
wdenk's avatar
wdenk committed
147
{
148
	int size = *(ext + 1);
wdenk's avatar
wdenk committed
149

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

153
	NetBootFileSize = 0;
wdenk's avatar
wdenk committed
154

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

250
static void BootpVendorProcess(u8 *ext, int size)
wdenk's avatar
wdenk committed
251
{
252
	u8 *end = ext + size;
wdenk's avatar
wdenk committed
253

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

256 257 258 259 260 261 262 263
	while ((ext < end) && (*ext != 0xff)) {
		if (*ext == 0) {
			ext++;
		} else {
			u8 *opt = ext;

			ext += ext[1] + 2;
			if (ext <= end)
264
				BootpVendorFieldProcess(opt);
265
		}
wdenk's avatar
wdenk committed
266 267
	}

268
	debug("[BOOTP] Received fields:\n");
269
	if (NetOurSubnetMask)
Robin Getz's avatar
Robin Getz committed
270
		debug("NetOurSubnetMask : %pI4\n", &NetOurSubnetMask);
271

272
	if (NetOurGatewayIP)
Robin Getz's avatar
Robin Getz committed
273
		debug("NetOurGatewayIP	: %pI4", &NetOurGatewayIP);
274

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

Robin Getz's avatar
Robin Getz committed
278 279
	if (NetOurHostName[0])
		debug("NetOurHostName  : %s\n", NetOurHostName);
280

Robin Getz's avatar
Robin Getz committed
281 282
	if (NetOurRootPath[0])
		debug("NetOurRootPath  : %s\n", NetOurRootPath);
283

Robin Getz's avatar
Robin Getz committed
284 285
	if (NetOurNISDomain[0])
		debug("NetOurNISDomain : %s\n", NetOurNISDomain);
286

Robin Getz's avatar
Robin Getz committed
287 288
	if (NetBootFileSize)
		debug("NetBootFileSize: %d\n", NetBootFileSize);
289 290 291 292 293

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

wdenk's avatar
wdenk committed
296 297 298 299
/*
 *	Handle a BOOTP received packet.
 */
static void
300 301
BootpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
	     unsigned len)
wdenk's avatar
wdenk committed
302
{
303
	struct Bootp_t *bp;
wdenk's avatar
wdenk committed
304

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

308
	bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
309

310 311
	/* Filter out pkts we don't want */
	if (BootpCheckPkt(pkt, dest, src, len))
wdenk's avatar
wdenk committed
312 313 314
		return;

	/*
315
	 *	Got a good BOOTP reply.	 Copy the data into our variables.
wdenk's avatar
wdenk committed
316 317
	 */
#ifdef CONFIG_STATUS_LED
318
	status_led_set(STATUS_LED_BOOT, STATUS_LED_OFF);
wdenk's avatar
wdenk committed
319 320 321 322 323
#endif

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

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

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

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

332
	net_auto_load();
wdenk's avatar
wdenk committed
333
}
334
#endif
wdenk's avatar
wdenk committed
335 336 337 338 339 340 341

/*
 *	Timeout on BOOTP/DHCP request.
 */
static void
BootpTimeout(void)
{
342 343 344
	ulong time_taken = get_timer(bootp_start);

	if (time_taken >= TIMEOUT_MS) {
345
#ifdef CONFIG_BOOTP_MAY_FAIL
346
		puts("\nRetry time exceeded\n");
347
		net_set_state(NETLOOP_FAIL);
348
#else
349
		puts("\nRetry time exceeded; starting again\n");
350
		NetStartAgain();
351
#endif
wdenk's avatar
wdenk committed
352
	} else {
353 354 355 356
		bootp_timeout *= 2;
		if (bootp_timeout > 1000)
			bootp_timeout = 1000;
		NetSetTimeout(bootp_timeout, BootpTimeout);
357
		BootpRequest();
wdenk's avatar
wdenk committed
358 359 360
	}
}

361 362 363 364 365 366 367 368 369
#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
370 371 372
/*
 *	Initialize BOOTP extension fields in the request.
 */
373
#if defined(CONFIG_CMD_DHCP)
374 375
static int DhcpExtended(u8 *e, int message_type, IPaddr_t ServerID,
			IPaddr_t RequestedIP)
wdenk's avatar
wdenk committed
376
{
377 378
	u8 *start = e;
	u8 *cnt;
379 380 381 382
#if defined(CONFIG_BOOTP_PXE)
	char *uuid;
	u16 clientarch;
#endif
383

384
#if defined(CONFIG_BOOTP_VENDOREX)
385
	u8 *x;
wdenk's avatar
wdenk committed
386
#endif
387
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
Wolfgang Denk's avatar
Wolfgang Denk committed
388
	char *hostname;
389
#endif
wdenk's avatar
wdenk committed
390

391 392 393 394
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
395

396 397 398
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = message_type;
wdenk's avatar
wdenk committed
399

400 401
	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
402 403
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
wdenk's avatar
wdenk committed
404

405
	if (ServerID) {
406
		int tmp = ntohl(ServerID);
wdenk's avatar
wdenk committed
407

408 409 410 411 412 413 414
		*e++ = 54;	/* ServerID */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
wdenk's avatar
wdenk committed
415

416
	if (RequestedIP) {
417
		int tmp = ntohl(RequestedIP);
wdenk's avatar
wdenk committed
418

419 420 421 422 423 424 425
		*e++ = 50;	/* Requested IP */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
426
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
427 428 429
	hostname = getenv("hostname");
	if (hostname) {
		int hostnamelen = strlen(hostname);
430 431 432

		*e++ = 12;	/* Hostname */
		*e++ = hostnamelen;
433
		memcpy(e, hostname, hostnamelen);
434 435
		e += hostnamelen;
	}
436 437
#endif

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
#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 */

459
			uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD);
460 461 462 463 464
			e += 16;
		} else {
			printf("Invalid pxeuuid: %s\n", uuid);
		}
	}
465
#endif
466

467 468
#ifdef CONFIG_BOOTP_VCI_STRING
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
469 470
#endif

471
#if defined(CONFIG_BOOTP_VENDOREX)
472 473
	x = dhcp_vendorex_prep(e);
	if (x)
474
		return x - start;
wdenk's avatar
wdenk committed
475 476
#endif

477 478 479
	*e++ = 55;		/* Parameter Request List */
	 cnt = e++;		/* Pointer to count of requested items */
	*cnt = 0;
480
#if defined(CONFIG_BOOTP_SUBNETMASK)
481 482
	*e++  = 1;		/* Subnet Mask */
	*cnt += 1;
wdenk's avatar
wdenk committed
483
#endif
484
#if defined(CONFIG_BOOTP_TIMEOFFSET)
485 486 487
	*e++  = 2;
	*cnt += 1;
#endif
488
#if defined(CONFIG_BOOTP_GATEWAY)
489 490
	*e++  = 3;		/* Router Option */
	*cnt += 1;
wdenk's avatar
wdenk committed
491
#endif
492
#if defined(CONFIG_BOOTP_DNS)
493 494
	*e++  = 6;		/* DNS Server(s) */
	*cnt += 1;
wdenk's avatar
wdenk committed
495
#endif
496
#if defined(CONFIG_BOOTP_HOSTNAME)
497 498
	*e++  = 12;		/* Hostname */
	*cnt += 1;
wdenk's avatar
wdenk committed
499
#endif
500
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
501 502
	*e++  = 13;		/* Boot File Size */
	*cnt += 1;
wdenk's avatar
wdenk committed
503
#endif
504
#if defined(CONFIG_BOOTP_BOOTPATH)
505 506
	*e++  = 17;		/* Boot path */
	*cnt += 1;
wdenk's avatar
wdenk committed
507
#endif
508
#if defined(CONFIG_BOOTP_NISDOMAIN)
509 510
	*e++  = 40;		/* NIS Domain name request */
	*cnt += 1;
511
#endif
512
#if defined(CONFIG_BOOTP_NTPSERVER)
513 514
	*e++  = 42;
	*cnt += 1;
wdenk's avatar
wdenk committed
515
#endif
516 517 518 519
	/* no options, so back up to avoid sending an empty request list */
	if (*cnt == 0)
		e -= 2;

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

522
	/* Pad to minimal length */
wdenk's avatar
wdenk committed
523
#ifdef	CONFIG_DHCP_MIN_EXT_LEN
524
	while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN)
525
		*e++ = 0;
wdenk's avatar
wdenk committed
526 527
#endif

528
	return e - start;
wdenk's avatar
wdenk committed
529 530
}

531
#else
wdenk's avatar
wdenk committed
532
/*
533
 * Warning: no field size check - change CONFIG_BOOTP_* at your own risk!
wdenk's avatar
wdenk committed
534
 */
535
static int BootpExtended(u8 *e)
wdenk's avatar
wdenk committed
536
{
537
	u8 *start = e;
wdenk's avatar
wdenk committed
538

539 540 541 542
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
543

544
#if defined(CONFIG_CMD_DHCP)
545 546 547 548 549 550
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = DHCP_DISCOVER;

	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
551 552
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
553
#endif
wdenk's avatar
wdenk committed
554

555 556 557 558 559
#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
560 561
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
#endif
562
#endif
563

564
#if defined(CONFIG_BOOTP_SUBNETMASK)
565 566 567
	*e++ = 1;		/* Subnet mask request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
568 569
#endif

570
#if defined(CONFIG_BOOTP_GATEWAY)
571 572 573
	*e++ = 3;		/* Default gateway request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
574 575
#endif

576
#if defined(CONFIG_BOOTP_DNS)
577 578 579
	*e++ = 6;		/* Domain Name Server */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
580 581
#endif

582
#if defined(CONFIG_BOOTP_HOSTNAME)
583 584 585
	*e++ = 12;		/* Host name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
586 587
#endif

588
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
589 590 591
	*e++ = 13;		/* Boot file size */
	*e++ = 2;
	e   += 2;
wdenk's avatar
wdenk committed
592 593
#endif

594
#if defined(CONFIG_BOOTP_BOOTPATH)
595 596 597
	*e++ = 17;		/* Boot path */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
598 599
#endif

600
#if defined(CONFIG_BOOTP_NISDOMAIN)
601 602 603
	*e++ = 40;		/* NIS Domain name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
604
#endif
605 606 607 608 609
#if defined(CONFIG_BOOTP_NTPSERVER)
	*e++ = 42;
	*e++ = 4;
	e   += 4;
#endif
wdenk's avatar
wdenk committed
610

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

613
	return e - start;
wdenk's avatar
wdenk committed
614
}
615
#endif
wdenk's avatar
wdenk committed
616

617 618 619 620 621 622 623
void BootpReset(void)
{
	BootpTry = 0;
	bootp_start = get_timer(0);
	bootp_timeout = 10;
}

wdenk's avatar
wdenk committed
624
void
625
BootpRequest(void)
wdenk's avatar
wdenk committed
626
{
627
	uchar *pkt, *iphdr;
628
	struct Bootp_t *bp;
629 630
	int extlen, pktlen, iplen;
	int eth_hdr_size;
631
#ifdef CONFIG_BOOTP_RANDOM_DELAY
Pavel Machek's avatar
Pavel Machek committed
632
	ulong rand_ms;
633
#endif
wdenk's avatar
wdenk committed
634

635
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
636
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
637 638 639 640
	dhcp_state = INIT;
#endif

#ifdef CONFIG_BOOTP_RANDOM_DELAY		/* Random BOOTP delay */
641 642
	if (BootpTry == 0)
		srand_mac();
wdenk's avatar
wdenk committed
643

644 645 646 647
	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
648

649
	printf("Random delay: %ld ms...\n", rand_ms);
Pavel Machek's avatar
Pavel Machek committed
650
	mdelay(rand_ms);
651

wdenk's avatar
wdenk committed
652 653 654 655
#endif	/* CONFIG_BOOTP_RANDOM_DELAY */

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

658 659
	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_IP);
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
660 661

	/*
662 663 664 665
	 * 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
666 667
	 * C. Hallinan, DS4.COM, Inc.
	 */
668
	/* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC,
669
		sizeof (struct Bootp_t)); */
670
	iphdr = pkt;	/* We need this later for net_set_udp_header() */
671
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
672

673
	bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
674 675 676 677
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
678
	bp->bp_secs = htons(get_timer(0) / 1000);
wdenk's avatar
wdenk committed
679 680 681 682
	NetWriteIP(&bp->bp_ciaddr, 0);
	NetWriteIP(&bp->bp_yiaddr, 0);
	NetWriteIP(&bp->bp_siaddr, 0);
	NetWriteIP(&bp->bp_giaddr, 0);
683 684
	memcpy(bp->bp_chaddr, NetOurEther, 6);
	copy_filename(bp->bp_file, BootFile, sizeof(bp->bp_file));
wdenk's avatar
wdenk committed
685 686

	/* Request additional information from the BOOTP/DHCP server */
687
#if defined(CONFIG_CMD_DHCP)
688
	extlen = DhcpExtended((u8 *)bp->bp_vend, DHCP_DISCOVER, 0, 0);
wdenk's avatar
wdenk committed
689
#else
690
	extlen = BootpExtended((u8 *)bp->bp_vend);
691
#endif
wdenk's avatar
wdenk committed
692 693 694

	/*
	 *	Bootp ID is the lower 4 bytes of our ethernet address
695
	 *	plus the current time in ms.
wdenk's avatar
wdenk committed
696 697 698 699 700 701
	 */
	BootpID = ((ulong)NetOurEther[2] << 24)
		| ((ulong)NetOurEther[3] << 16)
		| ((ulong)NetOurEther[4] << 8)
		| (ulong)NetOurEther[5];
	BootpID += get_timer(0);
702
	BootpID	 = htonl(BootpID);
wdenk's avatar
wdenk committed
703 704 705 706 707 708
	NetCopyLong(&bp->bp_id, &BootpID);

	/*
	 * Calculate proper packet lengths taking into account the
	 * variable size of the options field
	 */
709 710
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
711
	net_set_udp_header(iphdr, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, iplen);
712
	NetSetTimeout(bootp_timeout, BootpTimeout);
wdenk's avatar
wdenk committed
713

714
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
715
	dhcp_state = SELECTING;
716
	net_set_udp_handler(DhcpHandler);
wdenk's avatar
wdenk committed
717
#else
718
	net_set_udp_handler(BootpHandler);
719
#endif
wdenk's avatar
wdenk committed
720 721 722
	NetSendPacket(NetTxPacket, pktlen);
}

723
#if defined(CONFIG_CMD_DHCP)
724
static void DhcpOptionsProcess(uchar *popt, struct Bootp_t *bp)
wdenk's avatar
wdenk committed
725
{
726
	uchar *end = popt + BOOTP_HDR_SIZE;
wdenk's avatar
wdenk committed
727
	int oplen, size;
728 729 730
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
	int *to_ptr;
#endif
wdenk's avatar
wdenk committed
731

732
	while (popt < end && *popt != 0xff) {
wdenk's avatar
wdenk committed
733
		oplen = *(popt + 1);
734 735
		switch (*popt) {
		case 1:
736
			NetCopyIP(&NetOurSubnetMask, (popt + 2));
737
			break;
738
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
739
		case 2:		/* Time offset	*/
740
			to_ptr = &NetTimeOffset;
741 742
			NetCopyLong((ulong *)to_ptr, (ulong *)(popt + 2));
			NetTimeOffset = ntohl(NetTimeOffset);
743 744
			break;
#endif
745
		case 3:
746
			NetCopyIP(&NetOurGatewayIP, (popt + 2));
747 748
			break;
		case 6:
749
			NetCopyIP(&NetOurDNSIP, (popt + 2));
750
#if defined(CONFIG_BOOTP_DNS2)
751 752
			if (*(popt + 1) > 4)
				NetCopyIP(&NetOurDNS2IP, (popt + 2 + 4));
753
#endif
754 755
			break;
		case 12:
756 757 758
			size = truncate_sz("Host Name",
				sizeof(NetOurHostName), oplen);
			memcpy(&NetOurHostName, popt + 2, size);
759 760 761 762 763
			NetOurHostName[size] = 0;
			break;
		case 15:	/* Ignore Domain Name Option */
			break;
		case 17:
764 765 766
			size = truncate_sz("Root Path",
				sizeof(NetOurRootPath), oplen);
			memcpy(&NetOurRootPath, popt + 2, size);
767 768
			NetOurRootPath[size] = 0;
			break;
769 770
		case 28:	/* Ignore Broadcast Address Option */
			break;
771
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
772
		case 42:	/* NTP server IP */
773
			NetCopyIP(&NetNtpServerIP, (popt + 2));
774 775
			break;
#endif
776
		case 51:
777
			NetCopyLong(&dhcp_leasetime, (ulong *) (popt + 2));
778 779 780 781
			break;
		case 53:	/* Ignore Message Type Option */
			break;
		case 54:
782
			NetCopyIP(&NetDHCPServerIP, (popt + 2));
783 784 785 786 787
			break;
		case 58:	/* Ignore Renewal Time Option */
			break;
		case 59:	/* Ignore Rebinding Time Option */
			break;
788 789 790 791 792 793 794 795 796
		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
			 */
797
			size = truncate_sz("Opt Boot File",
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
					    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;
816
		default:
817
#if defined(CONFIG_BOOTP_VENDOREX)
818
			if (dhcp_vendorex_proc(popt))
wdenk's avatar
wdenk committed
819
				break;
wdenk's avatar
wdenk committed
820
#endif
821 822
			printf("*** Unhandled DHCP Option in OFFER/ACK:"
				" %d\n", *popt);
823
			break;
wdenk's avatar
wdenk committed
824 825 826 827 828 829 830
		}
		popt += oplen + 2;	/* Process next option */
	}
}

static int DhcpMessageType(unsigned char *popt)
{
831
	if (NetReadLong((ulong *)popt) != htonl(BOOTP_VENDOR_MAGIC))
wdenk's avatar
wdenk committed
832 833 834
		return -1;

	popt += 4;
835 836
	while (*popt != 0xff) {
		if (*popt == 53)	/* DHCP Message Type */
wdenk's avatar
wdenk committed
837 838 839 840 841 842
			return *(popt + 2);
		popt += *(popt + 1) + 2;	/* Scan through all options */
	}
	return -1;
}

843
static void DhcpSendRequestPkt(struct Bootp_t *bp_offer)
wdenk's avatar
wdenk committed
844
{
845
	uchar *pkt, *iphdr;
846
	struct Bootp_t *bp;
wdenk's avatar
wdenk committed
847
	int pktlen, iplen, extlen;
848
	int eth_hdr_size;
849
	IPaddr_t OfferedIP;
wdenk's avatar
wdenk committed
850

Robin Getz's avatar
Robin Getz committed
851
	debug("DhcpSendRequestPkt: Sending DHCPREQUEST\n");
wdenk's avatar
wdenk committed
852
	pkt = NetTxPacket;
853
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
854

855 856
	eth_hdr_size = NetSetEther(pkt, NetBcastAddr, PROT_IP);
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
857

858
	iphdr = pkt;	/* We'll need this later to set proper pkt size */
859
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
860

861
	bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
862 863 864 865
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
866
	bp->bp_secs = htons(get_timer(0) / 1000);
867 868
	/* 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
869

Wolfgang Denk's avatar
Wolfgang Denk committed
870
	/*
871 872 873 874 875
	 * RFC3046 requires Relay Agents to discard packets with
	 * nonzero and offered giaddr
	 */
	NetWriteIP(&bp->bp_giaddr, 0);

876
	memcpy(bp->bp_chaddr, NetOurEther, 6);
wdenk's avatar
wdenk committed
877 878 879 880 881 882 883 884 885 886

	/*
	 * 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
887 888 889

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

893 894
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
895
	net_set_udp_header(iphdr, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC, iplen);
wdenk's avatar
wdenk committed
896

897 898 899
#ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY
	udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY);
#endif	/* CONFIG_BOOTP_DHCP_REQUEST_DELAY */
900
	debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen);
wdenk's avatar
wdenk committed
901 902 903 904 905 906 907
	NetSendPacket(NetTxPacket, pktlen);
}

/*
 *	Handle DHCP received packets.
 */
static void
908 909
DhcpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
	    unsigned len)
wdenk's avatar
wdenk committed
910
{
911
	struct Bootp_t *bp = (struct Bootp_t *)pkt;
wdenk's avatar
wdenk committed
912

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

916 917
	/* Filter out pkts we don't want */
	if (BootpCheckPkt(pkt, dest, src, len))
wdenk's avatar
wdenk committed
918 919
		return;

920 921
	debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state:"
		" %d\n", src, dest, len, dhcp_state);
wdenk's avatar
wdenk committed
922 923 924 925 926

	switch (dhcp_state) {
	case SELECTING:
		/*
		 * Wait an appropriate time for any potential DHCPOFFER packets
927 928 929
		 * 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
930
		 */
Robin Getz's avatar
Robin Getz committed
931
		debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file);
932
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
933
		if (strncmp(bp->bp_file,
934
			    CONFIG_SYS_BOOTFILE_PREFIX,
935
			    strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
936
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
wdenk's avatar
wdenk committed
937

Robin Getz's avatar
Robin Getz committed
938
			debug("TRANSITIONING TO REQUESTING STATE\n");