bootp.c 25.6 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
 */

#include <common.h>
#include <command.h>
#include <net.h>
14
#include <net/tftp.h>
wdenk's avatar
wdenk committed
15
#include "bootp.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
#endif

50 51 52 53
#ifndef CONFIG_BOOTP_ID_CACHE_SIZE
#define CONFIG_BOOTP_ID_CACHE_SIZE 4
#endif

54
u32		bootp_ids[CONFIG_BOOTP_ID_CACHE_SIZE];
55
unsigned int	bootp_num_ids;
56
int		bootp_try;
57 58
ulong		bootp_start;
ulong		bootp_timeout;
59 60 61
char net_nis_domain[32] = {0,}; /* Our NIS domain */
char net_hostname[32] = {0,}; /* Our hostname */
char net_root_path[64] = {0,}; /* Our bootpath */
wdenk's avatar
wdenk committed
62

63 64
static ulong time_taken_max;

65
#if defined(CONFIG_CMD_DHCP)
Kim Phillips's avatar
Kim Phillips committed
66
static dhcp_state_t dhcp_state = INIT;
67
static u32 dhcp_leasetime;
68
static struct in_addr dhcp_server_ip;
69 70 71
static u8 dhcp_option_overload;
#define OVERLOAD_FILE 1
#define OVERLOAD_SNAME 2
72 73
static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
			unsigned src, unsigned len);
wdenk's avatar
wdenk committed
74 75

/* For Debug */
76 77
#if 0
static char *dhcpmsg2str(int type)
wdenk's avatar
wdenk committed
78 79
{
	switch (type) {
80 81 82 83 84 85 86
	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
87 88 89
	default: return "UNKNOWN/INVALID MSG TYPE"; break;
	}
}
90
#endif
91
#endif
wdenk's avatar
wdenk committed
92

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
static void bootp_add_id(ulong id)
{
	if (bootp_num_ids >= ARRAY_SIZE(bootp_ids)) {
		size_t size = sizeof(bootp_ids) - sizeof(id);

		memmove(bootp_ids, &bootp_ids[1], size);
		bootp_ids[bootp_num_ids - 1] = id;
	} else {
		bootp_ids[bootp_num_ids] = id;
		bootp_num_ids++;
	}
}

static bool bootp_match_id(ulong id)
{
	unsigned int i;

	for (i = 0; i < bootp_num_ids; i++)
		if (bootp_ids[i] == id)
			return true;

	return false;
}

117 118
static int check_reply_packet(uchar *pkt, unsigned dest, unsigned src,
			      unsigned len)
wdenk's avatar
wdenk committed
119
{
120
	struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
121 122 123 124
	int retval = 0;

	if (dest != PORT_BOOTPC || src != PORT_BOOTPS)
		retval = -1;
125
	else if (len < sizeof(struct bootp_hdr) - OPT_FIELD_SIZE)
wdenk's avatar
wdenk committed
126
		retval = -2;
127
	else if (bp->bp_op != OP_BOOTREPLY)
wdenk's avatar
wdenk committed
128 129 130 131 132
		retval = -3;
	else if (bp->bp_htype != HWT_ETHER)
		retval = -4;
	else if (bp->bp_hlen != HWL_ETHER)
		retval = -5;
133
	else if (!bootp_match_id(net_read_u32(&bp->bp_id)))
wdenk's avatar
wdenk committed
134 135
		retval = -6;

136
	debug("Filtering pkt = %d\n", retval);
wdenk's avatar
wdenk committed
137 138 139 140 141 142 143

	return retval;
}

/*
 * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet
 */
144
static void store_net_params(struct bootp_hdr *bp)
wdenk's avatar
wdenk committed
145
{
146
#if !defined(CONFIG_BOOTP_SERVERIP)
147
	struct in_addr tmp_ip;
148

149 150 151
	net_copy_ip(&tmp_ip, &bp->bp_siaddr);
	if (tmp_ip.s_addr != 0)
		net_copy_ip(&net_server_ip, &bp->bp_siaddr);
152 153
	memcpy(net_server_ethaddr,
	       ((struct ethernet_hdr *)net_rx_packet)->et_src, 6);
154 155 156 157 158
	if (
#if defined(CONFIG_CMD_DHCP)
	    !(dhcp_option_overload & OVERLOAD_FILE) &&
#endif
	    (strlen(bp->bp_file) > 0)) {
159 160
		copy_filename(net_boot_file_name, bp->bp_file,
			      sizeof(net_boot_file_name));
161
	}
wdenk's avatar
wdenk committed
162

163
	debug("net_boot_file_name: %s\n", net_boot_file_name);
wdenk's avatar
wdenk committed
164 165

	/* Propagate to environment:
wdenk's avatar
wdenk committed
166
	 * don't delete exising entry when BOOTP / DHCP reply does
wdenk's avatar
wdenk committed
167 168
	 * not contain a new value
	 */
169 170
	if (*net_boot_file_name)
		setenv("bootfile", net_boot_file_name);
171
#endif
172
	net_copy_ip(&net_ip, &bp->bp_yiaddr);
wdenk's avatar
wdenk committed
173 174
}

175
static int truncate_sz(const char *name, int maxlen, int curlen)
wdenk's avatar
wdenk committed
176 177
{
	if (curlen >= maxlen) {
178 179
		printf("*** WARNING: %s is too long (%d - max: %d)"
			" - truncated\n", name, curlen, maxlen);
wdenk's avatar
wdenk committed
180 181
		curlen = maxlen - 1;
	}
182
	return curlen;
wdenk's avatar
wdenk committed
183 184
}

185
#if !defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
186

187
static void bootp_process_vendor_field(u8 *ext)
wdenk's avatar
wdenk committed
188
{
189
	int size = *(ext + 1);
wdenk's avatar
wdenk committed
190

191
	debug("[BOOTP] Processing extension %d... (%d bytes)\n", *ext,
192
	      *(ext + 1));
wdenk's avatar
wdenk committed
193

194
	net_boot_file_expected_size_in_blocks = 0;
wdenk's avatar
wdenk committed
195

196 197
	switch (*ext) {
		/* Fixed length fields */
198
	case 1:			/* Subnet mask */
199 200
		if (net_netmask.s_addr == 0)
			net_copy_ip(&net_netmask, (struct in_addr *)(ext + 2));
wdenk's avatar
wdenk committed
201
		break;
202
	case 2:			/* Time offset - Not yet supported */
wdenk's avatar
wdenk committed
203
		break;
204
		/* Variable length fields */
205
	case 3:			/* Gateways list */
206 207
		if (net_gateway.s_addr == 0)
			net_copy_ip(&net_gateway, (struct in_addr *)(ext + 2));
wdenk's avatar
wdenk committed
208
		break;
209
	case 4:			/* Time server - Not yet supported */
wdenk's avatar
wdenk committed
210
		break;
211
	case 5:			/* IEN-116 name server - Not yet supported */
wdenk's avatar
wdenk committed
212 213
		break;
	case 6:
214 215 216
		if (net_dns_server.s_addr == 0)
			net_copy_ip(&net_dns_server,
				    (struct in_addr *)(ext + 2));
217
#if defined(CONFIG_BOOTP_DNS2)
218 219 220
		if ((net_dns_server2.s_addr == 0) && (size > 4))
			net_copy_ip(&net_dns_server2,
				    (struct in_addr *)(ext + 2 + 4));
221
#endif
wdenk's avatar
wdenk committed
222
		break;
223
	case 7:			/* Log server - Not yet supported */
wdenk's avatar
wdenk committed
224
		break;
225
	case 8:			/* Cookie/Quote server - Not yet supported */
wdenk's avatar
wdenk committed
226
		break;
227
	case 9:			/* LPR server - Not yet supported */
wdenk's avatar
wdenk committed
228
		break;
229
	case 10:		/* Impress server - Not yet supported */
wdenk's avatar
wdenk committed
230
		break;
231
	case 11:		/* RPL server - Not yet supported */
wdenk's avatar
wdenk committed
232
		break;
233
	case 12:		/* Host name */
234
		if (net_hostname[0] == 0) {
235
			size = truncate_sz("Host Name",
236 237 238
				sizeof(net_hostname), size);
			memcpy(&net_hostname, ext + 2, size);
			net_hostname[size] = 0;
wdenk's avatar
wdenk committed
239 240
		}
		break;
241
	case 13:		/* Boot file size */
wdenk's avatar
wdenk committed
242
		if (size == 2)
243 244
			net_boot_file_expected_size_in_blocks =
				ntohs(*(ushort *)(ext + 2));
wdenk's avatar
wdenk committed
245
		else if (size == 4)
246 247
			net_boot_file_expected_size_in_blocks =
				ntohl(*(ulong *)(ext + 2));
wdenk's avatar
wdenk committed
248
		break;
249
	case 14:		/* Merit dump file - Not yet supported */
wdenk's avatar
wdenk committed
250
		break;
251
	case 15:		/* Domain name - Not yet supported */
wdenk's avatar
wdenk committed
252
		break;
253
	case 16:		/* Swap server - Not yet supported */
wdenk's avatar
wdenk committed
254
		break;
255
	case 17:		/* Root path */
256
		if (net_root_path[0] == 0) {
257
			size = truncate_sz("Root Path",
258 259 260
				sizeof(net_root_path), size);
			memcpy(&net_root_path, ext + 2, size);
			net_root_path[size] = 0;
wdenk's avatar
wdenk committed
261 262
		}
		break;
263
	case 18:		/* Extension path - Not yet supported */
wdenk's avatar
wdenk committed
264
		/*
wdenk's avatar
wdenk committed
265 266 267
		 * 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
268 269
		 */
		break;
270
		/* IP host layer fields */
271
	case 40:		/* NIS Domain name */
272
		if (net_nis_domain[0] == 0) {
273
			size = truncate_sz("NIS Domain Name",
274 275 276
				sizeof(net_nis_domain), size);
			memcpy(&net_nis_domain, ext + 2, size);
			net_nis_domain[size] = 0;
wdenk's avatar
wdenk committed
277 278
		}
		break;
279 280
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
	case 42:	/* NTP server IP */
281
		net_copy_ip(&net_ntp_server, (struct in_addr *)(ext + 2));
282 283
		break;
#endif
284
		/* Application layer fields */
285
	case 43:		/* Vendor specific info - Not yet supported */
wdenk's avatar
wdenk committed
286
		/*
wdenk's avatar
wdenk committed
287 288
		 * Binary information to exchange specific
		 * product information.
wdenk's avatar
wdenk committed
289 290
		 */
		break;
291 292
		/* Reserved (custom) fields (128..254) */
	}
wdenk's avatar
wdenk committed
293 294
}

295
static void bootp_process_vendor(u8 *ext, int size)
wdenk's avatar
wdenk committed
296
{
297
	u8 *end = ext + size;
wdenk's avatar
wdenk committed
298

299
	debug("[BOOTP] Checking extension (%d bytes)...\n", size);
wdenk's avatar
wdenk committed
300

301 302 303 304 305 306 307 308
	while ((ext < end) && (*ext != 0xff)) {
		if (*ext == 0) {
			ext++;
		} else {
			u8 *opt = ext;

			ext += ext[1] + 2;
			if (ext <= end)
309
				bootp_process_vendor_field(opt);
310
		}
wdenk's avatar
wdenk committed
311 312
	}

313
	debug("[BOOTP] Received fields:\n");
314 315
	if (net_netmask.s_addr)
		debug("net_netmask : %pI4\n", &net_netmask);
316

317 318
	if (net_gateway.s_addr)
		debug("net_gateway	: %pI4", &net_gateway);
319

320 321 322
	if (net_boot_file_expected_size_in_blocks)
		debug("net_boot_file_expected_size_in_blocks : %d\n",
		      net_boot_file_expected_size_in_blocks);
wdenk's avatar
wdenk committed
323

324 325
	if (net_hostname[0])
		debug("net_hostname  : %s\n", net_hostname);
326

327 328
	if (net_root_path[0])
		debug("net_root_path  : %s\n", net_root_path);
329

330 331
	if (net_nis_domain[0])
		debug("net_nis_domain : %s\n", net_nis_domain);
332

333
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
334 335
	if (net_ntp_server)
		debug("net_ntp_server : %pI4\n", &net_ntp_server);
336
#endif
337
}
338

wdenk's avatar
wdenk committed
339 340 341
/*
 *	Handle a BOOTP received packet.
 */
342 343
static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
			  unsigned src, unsigned len)
wdenk's avatar
wdenk committed
344
{
345
	struct bootp_hdr *bp;
wdenk's avatar
wdenk committed
346

347
	debug("got BOOTP packet (src=%d, dst=%d, len=%d want_len=%zu)\n",
348
	      src, dest, len, sizeof(struct bootp_hdr));
wdenk's avatar
wdenk committed
349

350
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
351

352
	/* Filter out pkts we don't want */
353
	if (check_reply_packet(pkt, dest, src, len))
wdenk's avatar
wdenk committed
354 355 356
		return;

	/*
357
	 *	Got a good BOOTP reply.	 Copy the data into our variables.
wdenk's avatar
wdenk committed
358
	 */
359
#if defined(CONFIG_STATUS_LED) && defined(STATUS_LED_BOOT)
360
	status_led_set(STATUS_LED_BOOT, STATUS_LED_OFF);
wdenk's avatar
wdenk committed
361 362
#endif

363
	store_net_params(bp);		/* Store net parameters from reply */
wdenk's avatar
wdenk committed
364 365

	/* Retrieve extended information (we must parse the vendor area) */
366
	if (net_read_u32((u32 *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC))
367
		bootp_process_vendor((uchar *)&bp->bp_vend[4], len);
wdenk's avatar
wdenk committed
368

369
	net_set_timeout_handler(0, (thand_f *)0);
370
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
wdenk's avatar
wdenk committed
371

372
	debug("Got good BOOTP\n");
wdenk's avatar
wdenk committed
373

374
	net_auto_load();
wdenk's avatar
wdenk committed
375
}
376
#endif
wdenk's avatar
wdenk committed
377 378 379 380

/*
 *	Timeout on BOOTP/DHCP request.
 */
381
static void bootp_timeout_handler(void)
wdenk's avatar
wdenk committed
382
{
383 384
	ulong time_taken = get_timer(bootp_start);

385
	if (time_taken >= time_taken_max) {
386
#ifdef CONFIG_BOOTP_MAY_FAIL
387
		puts("\nRetry time exceeded\n");
388
		net_set_state(NETLOOP_FAIL);
389
#else
390
		puts("\nRetry time exceeded; starting again\n");
391
		net_start_again();
392
#endif
wdenk's avatar
wdenk committed
393
	} else {
394
		bootp_timeout *= 2;
395 396
		if (bootp_timeout > 2000)
			bootp_timeout = 2000;
397
		net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
398
		bootp_request();
wdenk's avatar
wdenk committed
399 400 401
	}
}

402 403 404 405 406 407 408 409 410
#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
411 412 413
/*
 *	Initialize BOOTP extension fields in the request.
 */
414
#if defined(CONFIG_CMD_DHCP)
415 416
static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip,
			struct in_addr requested_ip)
wdenk's avatar
wdenk committed
417
{
418 419
	u8 *start = e;
	u8 *cnt;
420 421 422 423
#if defined(CONFIG_BOOTP_PXE)
	char *uuid;
	u16 clientarch;
#endif
424

425
#if defined(CONFIG_BOOTP_VENDOREX)
426
	u8 *x;
wdenk's avatar
wdenk committed
427
#endif
428
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
Wolfgang Denk's avatar
Wolfgang Denk committed
429
	char *hostname;
430
#endif
wdenk's avatar
wdenk committed
431

432 433 434 435
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
436

437 438 439
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = message_type;
wdenk's avatar
wdenk committed
440

441 442
	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
443 444
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
wdenk's avatar
wdenk committed
445

446 447
	if (server_ip.s_addr) {
		int tmp = ntohl(server_ip.s_addr);
wdenk's avatar
wdenk committed
448

449 450 451 452 453 454 455
		*e++ = 54;	/* ServerID */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
wdenk's avatar
wdenk committed
456

457 458
	if (requested_ip.s_addr) {
		int tmp = ntohl(requested_ip.s_addr);
wdenk's avatar
wdenk committed
459

460 461 462 463 464 465 466
		*e++ = 50;	/* Requested IP */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
467
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
468 469 470
	hostname = getenv("hostname");
	if (hostname) {
		int hostnamelen = strlen(hostname);
471 472 473

		*e++ = 12;	/* Hostname */
		*e++ = hostnamelen;
474
		memcpy(e, hostname, hostnamelen);
475 476
		e += hostnamelen;
	}
477 478
#endif

479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
#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 */

500
			uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD);
501 502 503 504 505
			e += 16;
		} else {
			printf("Invalid pxeuuid: %s\n", uuid);
		}
	}
506
#endif
507

508 509 510
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING)
	put_vci(e, CONFIG_SPL_NET_VCI_STRING);
#elif defined(CONFIG_BOOTP_VCI_STRING)
511
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
512 513
#endif

514
#if defined(CONFIG_BOOTP_VENDOREX)
515 516
	x = dhcp_vendorex_prep(e);
	if (x)
517
		return x - start;
wdenk's avatar
wdenk committed
518 519
#endif

520 521 522
	*e++ = 55;		/* Parameter Request List */
	 cnt = e++;		/* Pointer to count of requested items */
	*cnt = 0;
523
#if defined(CONFIG_BOOTP_SUBNETMASK)
524 525
	*e++  = 1;		/* Subnet Mask */
	*cnt += 1;
wdenk's avatar
wdenk committed
526
#endif
527
#if defined(CONFIG_BOOTP_TIMEOFFSET)
528 529 530
	*e++  = 2;
	*cnt += 1;
#endif
531
#if defined(CONFIG_BOOTP_GATEWAY)
532 533
	*e++  = 3;		/* Router Option */
	*cnt += 1;
wdenk's avatar
wdenk committed
534
#endif
535
#if defined(CONFIG_BOOTP_DNS)
536 537
	*e++  = 6;		/* DNS Server(s) */
	*cnt += 1;
wdenk's avatar
wdenk committed
538
#endif
539
#if defined(CONFIG_BOOTP_HOSTNAME)
540 541
	*e++  = 12;		/* Hostname */
	*cnt += 1;
wdenk's avatar
wdenk committed
542
#endif
543
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
544 545
	*e++  = 13;		/* Boot File Size */
	*cnt += 1;
wdenk's avatar
wdenk committed
546
#endif
547
#if defined(CONFIG_BOOTP_BOOTPATH)
548 549
	*e++  = 17;		/* Boot path */
	*cnt += 1;
wdenk's avatar
wdenk committed
550
#endif
551
#if defined(CONFIG_BOOTP_NISDOMAIN)
552 553
	*e++  = 40;		/* NIS Domain name request */
	*cnt += 1;
554
#endif
555
#if defined(CONFIG_BOOTP_NTPSERVER)
556 557
	*e++  = 42;
	*cnt += 1;
wdenk's avatar
wdenk committed
558
#endif
559 560 561 562
	/* no options, so back up to avoid sending an empty request list */
	if (*cnt == 0)
		e -= 2;

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

565
	/* Pad to minimal length */
wdenk's avatar
wdenk committed
566
#ifdef	CONFIG_DHCP_MIN_EXT_LEN
567
	while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN)
568
		*e++ = 0;
wdenk's avatar
wdenk committed
569 570
#endif

571
	return e - start;
wdenk's avatar
wdenk committed
572 573
}

574
#else
wdenk's avatar
wdenk committed
575
/*
576
 * Warning: no field size check - change CONFIG_BOOTP_* at your own risk!
wdenk's avatar
wdenk committed
577
 */
578
static int bootp_extended(u8 *e)
wdenk's avatar
wdenk committed
579
{
580
	u8 *start = e;
wdenk's avatar
wdenk committed
581

582 583 584 585
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
586

587
#if defined(CONFIG_CMD_DHCP)
588 589 590 591 592 593
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = DHCP_DISCOVER;

	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
594 595
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
596
#endif
wdenk's avatar
wdenk committed
597

598 599 600 601 602
#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
603 604
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
#endif
605
#endif
606

607
#if defined(CONFIG_BOOTP_SUBNETMASK)
608 609 610
	*e++ = 1;		/* Subnet mask request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
611 612
#endif

613
#if defined(CONFIG_BOOTP_GATEWAY)
614 615 616
	*e++ = 3;		/* Default gateway request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
617 618
#endif

619
#if defined(CONFIG_BOOTP_DNS)
620 621 622
	*e++ = 6;		/* Domain Name Server */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
623 624
#endif

625
#if defined(CONFIG_BOOTP_HOSTNAME)
626 627 628
	*e++ = 12;		/* Host name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
629 630
#endif

631
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
632 633 634
	*e++ = 13;		/* Boot file size */
	*e++ = 2;
	e   += 2;
wdenk's avatar
wdenk committed
635 636
#endif

637
#if defined(CONFIG_BOOTP_BOOTPATH)
638 639 640
	*e++ = 17;		/* Boot path */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
641 642
#endif

643
#if defined(CONFIG_BOOTP_NISDOMAIN)
644 645 646
	*e++ = 40;		/* NIS Domain name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
647
#endif
648 649 650 651 652
#if defined(CONFIG_BOOTP_NTPSERVER)
	*e++ = 42;
	*e++ = 4;
	e   += 4;
#endif
wdenk's avatar
wdenk committed
653

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

656
	return e - start;
wdenk's avatar
wdenk committed
657
}
658
#endif
wdenk's avatar
wdenk committed
659

660
void bootp_reset(void)
661
{
662
	bootp_num_ids = 0;
663
	bootp_try = 0;
664
	bootp_start = get_timer(0);
665
	bootp_timeout = 250;
666 667
}

668
void bootp_request(void)
wdenk's avatar
wdenk committed
669
{
670
	uchar *pkt, *iphdr;
671
	struct bootp_hdr *bp;
672 673
	int extlen, pktlen, iplen;
	int eth_hdr_size;
674
#ifdef CONFIG_BOOTP_RANDOM_DELAY
Pavel Machek's avatar
Pavel Machek committed
675
	ulong rand_ms;
676
#endif
677
	u32 bootp_id;
678 679
	struct in_addr zero_ip;
	struct in_addr bcast_ip;
680
	char *ep;  /* Environment pointer */
wdenk's avatar
wdenk committed
681

682
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
683
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
684 685 686
	dhcp_state = INIT;
#endif

687 688 689 690 691 692
	ep = getenv("bootpretryperiod");
	if (ep != NULL)
		time_taken_max = simple_strtoul(ep, NULL, 10);
	else
		time_taken_max = TIMEOUT_MS;

wdenk's avatar
wdenk committed
693
#ifdef CONFIG_BOOTP_RANDOM_DELAY		/* Random BOOTP delay */
694
	if (bootp_try == 0)
695
		srand_mac();
wdenk's avatar
wdenk committed
696

697 698
	if (bootp_try <= 2)	/* Start with max 1024 * 1ms */
		rand_ms = rand() >> (22 - bootp_try);
699 700
	else		/* After 3rd BOOTP request max 8192 * 1ms */
		rand_ms = rand() >> 19;
wdenk's avatar
wdenk committed
701

702
	printf("Random delay: %ld ms...\n", rand_ms);
Pavel Machek's avatar
Pavel Machek committed
703
	mdelay(rand_ms);
704

wdenk's avatar
wdenk committed
705 706
#endif	/* CONFIG_BOOTP_RANDOM_DELAY */

707
	printf("BOOTP broadcast %d\n", ++bootp_try);
708
	pkt = net_tx_packet;
709
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
710

711
	eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
712
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
713 714

	/*
715 716 717 718
	 * 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
719 720
	 * C. Hallinan, DS4.COM, Inc.
	 */
721
	/* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC,
722
		sizeof (struct bootp_hdr)); */
723
	iphdr = pkt;	/* We need this later for net_set_udp_header() */
724
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
725

726
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
727 728 729 730
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
731 732 733 734 735
	/*
	 * according to RFC1542, should be 0 on first request, secs since
	 * first request otherwise
	 */
	bp->bp_secs = htons(get_timer(bootp_start) / 1000);
736 737 738 739 740
	zero_ip.s_addr = 0;
	net_write_ip(&bp->bp_ciaddr, zero_ip);
	net_write_ip(&bp->bp_yiaddr, zero_ip);
	net_write_ip(&bp->bp_siaddr, zero_ip);
	net_write_ip(&bp->bp_giaddr, zero_ip);
741
	memcpy(bp->bp_chaddr, net_ethaddr, 6);
742
	copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
wdenk's avatar
wdenk committed
743 744

	/* Request additional information from the BOOTP/DHCP server */
745
#if defined(CONFIG_CMD_DHCP)
746 747
	extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_DISCOVER, zero_ip,
			       zero_ip);
wdenk's avatar
wdenk committed
748
#else
749
	extlen = bootp_extended((u8 *)bp->bp_vend);
750
#endif
wdenk's avatar
wdenk committed
751 752 753

	/*
	 *	Bootp ID is the lower 4 bytes of our ethernet address
754
	 *	plus the current time in ms.
wdenk's avatar
wdenk committed
755
	 */
756 757 758 759
	bootp_id = ((u32)net_ethaddr[2] << 24)
		| ((u32)net_ethaddr[3] << 16)
		| ((u32)net_ethaddr[4] << 8)
		| (u32)net_ethaddr[5];
760 761 762
	bootp_id += get_timer(0);
	bootp_id = htonl(bootp_id);
	bootp_add_id(bootp_id);
763
	net_copy_u32(&bp->bp_id, &bootp_id);
wdenk's avatar
wdenk committed
764 765 766 767 768

	/*
	 * Calculate proper packet lengths taking into account the
	 * variable size of the options field
	 */
769 770
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
771 772
	bcast_ip.s_addr = 0xFFFFFFFFL;
	net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
773
	net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
wdenk's avatar
wdenk committed
774

775
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
776
	dhcp_state = SELECTING;
777
	net_set_udp_handler(dhcp_handler);
wdenk's avatar
wdenk committed
778
#else
779
	net_set_udp_handler(bootp_handler);
780
#endif
781
	net_send_packet(net_tx_packet, pktlen);
wdenk's avatar
wdenk committed
782 783
}

784
#if defined(CONFIG_CMD_DHCP)
785
static void dhcp_process_options(uchar *popt, uchar *end)
wdenk's avatar
wdenk committed
786 787
{
	int oplen, size;
788 789 790
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
	int *to_ptr;
#endif
wdenk's avatar
wdenk committed
791

792
	while (popt < end && *popt != 0xff) {
wdenk's avatar
wdenk committed
793
		oplen = *(popt + 1);
794
		switch (*popt) {
795 796 797
		case 0:
			oplen = -1; /* Pad omits len byte */
			break;
798
		case 1:
799
			net_copy_ip(&net_netmask, (popt + 2));
800
			break;
801
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
802
		case 2:		/* Time offset	*/
803
			to_ptr = &net_ntp_time_offset;
804
			net_copy_u32((u32 *)to_ptr, (u32 *)(popt + 2));
805
			net_ntp_time_offset = ntohl(net_ntp_time_offset);
806 807
			break;
#endif
808
		case 3:
809
			net_copy_ip(&net_gateway, (popt + 2));
810 811
			break;
		case 6:
812
			net_copy_ip(&net_dns_server, (popt + 2));
813
#if defined(CONFIG_BOOTP_DNS2)
814
			if (*(popt + 1) > 4)
815
				net_copy_ip(&net_dns_server2, (popt + 2 + 4));
816
#endif
817 818
			break;
		case 12:
819
			size = truncate_sz("Host Name",
820 821 822
				sizeof(net_hostname), oplen);
			memcpy(&net_hostname, popt + 2, size);
			net_hostname[size] = 0;
823 824 825 826
			break;
		case 15:	/* Ignore Domain Name Option */
			break;
		case 17:
827
			size = truncate_sz("Root Path",
828 829 830
				sizeof(net_root_path), oplen);
			memcpy(&net_root_path, popt + 2, size);
			net_root_path[size] = 0;
831
			break;
832 833
		case 28:	/* Ignore Broadcast Address Option */
			break;
834
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
835
		case 42:	/* NTP server IP */
836
			net_copy_ip(&net_ntp_server, (popt + 2));
837 838
			break;
#endif
839
		case 51:
840
			net_copy_u32(&dhcp_leasetime, (u32 *)(popt + 2));
841
			break;
842 843 844
		case 52:
			dhcp_option_overload = popt[2];
			break;
845 846 847
		case 53:	/* Ignore Message Type Option */
			break;
		case 54:
848
			net_copy_ip(&dhcp_server_ip, (popt + 2));
849 850 851 852 853
			break;
		case 58:	/* Ignore Renewal Time Option */
			break;
		case 59:	/* Ignore Rebinding Time Option */
			break;
854 855
		case 66:	/* Ignore TFTP server name */
			break;
856 857 858 859 860
		case 67:	/* Bootfile option */
			size = truncate_sz("Bootfile",
					   sizeof(net_boot_file_name), oplen);
			memcpy(&net_boot_file_name, popt + 2, size);
			net_boot_file_name[size] = 0;
861
			break;
862
		default:
863
#if defined(CONFIG_BOOTP_VENDOREX)
864
			if (dhcp_vendorex_proc(popt))
wdenk's avatar
wdenk committed
865
				break;
wdenk's avatar
wdenk committed
866
#endif
867
			printf("*** Unhandled DHCP Option in OFFER/ACK:"
868
			       " %d\n", *popt);
869
			break;
wdenk's avatar
wdenk committed
870 871 872 873 874
		}
		popt += oplen + 2;	/* Process next option */
	}
}

875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
static void dhcp_packet_process_options(struct bootp_hdr *bp)
{
	uchar *popt = (uchar *)&bp->bp_vend[4];
	uchar *end = popt + BOOTP_HDR_SIZE;

	if (net_read_u32((u32 *)&bp->bp_vend[0]) != htonl(BOOTP_VENDOR_MAGIC))
		return;

	dhcp_option_overload = 0;

	/*
	 * The 'options' field MUST be interpreted first, 'file' next,
	 * 'sname' last.
	 */
	dhcp_process_options(popt, end);

	if (dhcp_option_overload & OVERLOAD_FILE) {
		popt = (uchar *)bp->bp_file;
		end = popt + sizeof(bp->bp_file);
		dhcp_process_options(popt, end);
	}

	if (dhcp_option_overload & OVERLOAD_SNAME) {
		popt = (uchar *)bp->bp_sname;
		end = popt + sizeof(bp->bp_sname);
		dhcp_process_options(popt, end);
	}
}

904
static int dhcp_message_type(unsigned char *popt)
wdenk's avatar
wdenk committed
905
{
906
	if (net_read_u32((u32 *)popt) != htonl(BOOTP_VENDOR_MAGIC))
wdenk's avatar
wdenk committed
907 908 909
		return -1;

	popt += 4;
910 911
	while (*popt != 0xff) {
		if (*popt == 53)	/* DHCP Message Type */
wdenk's avatar
wdenk committed
912
			return *(popt + 2);
913 914 915 916 917 918 919
		if (*popt == 0)	{
			/* Pad */
			popt += 1;
		} else {
			/* Scan through all options */
			popt += *(popt + 1) + 2;
		}
wdenk's avatar
wdenk committed
920 921 922 923
	}
	return -1;
}

924
static void dhcp_send_request_packet(struct bootp_hdr *bp_offer)
wdenk's avatar
wdenk committed
925
{
926
	uchar *pkt, *iphdr;
927
	struct bootp_hdr *bp;
wdenk's avatar
wdenk committed
928
	int pktlen, iplen, extlen;
929
	int eth_hdr_size;
930 931 932
	struct in_addr offered_ip;
	struct in_addr zero_ip;
	struct in_addr bcast_ip;
wdenk's avatar
wdenk committed
933

934
	debug("dhcp_send_request_packet: Sending DHCPREQUEST\n");
935
	pkt = net_tx_packet;
936
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
937

938
	eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
939
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
940

941
	iphdr = pkt;	/* We'll need this later to set proper pkt size */
942
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
943

944
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
945 946 947 948
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
949
	bp->bp_secs = htons(get_timer(bootp_start) / 1000);
950 951
	/* 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
952

Wolfgang Denk's avatar
Wolfgang Denk committed
953
	/*
954 955 956
	 * RFC3046 requires Relay Agents to discard packets with
	 * nonzero and offered giaddr
	 */
957 958
	zero_ip.s_addr = 0;
	net_write_ip(&bp->bp_giaddr, zero_ip);
959

960
	memcpy(bp->bp_chaddr, net_ethaddr, 6);
961
	copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
wdenk's avatar
wdenk committed
962 963 964 965 966

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

967
	net_copy_u32(&bp->bp_id, &bp_offer->bp_id);
wdenk's avatar
wdenk committed
968 969 970 971

	/*
	 * Copy options from OFFER packet if present
	 */
Justin Flammia's avatar
Justin Flammia committed
972 973

	/* Copy offered IP into the parameters request list */
974 975 976
	net_copy_ip(&offered_ip, &bp_offer->bp_yiaddr);
	extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_REQUEST,
		dhcp_server_ip, offered_ip);
wdenk's avatar
wdenk committed
977

978 979
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
980 981
	bcast_ip.s_addr = 0xFFFFFFFFL;
	net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
wdenk's avatar
wdenk committed
982

983 984 985
#ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY
	udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY);
#endif	/* CONFIG_BOOTP_DHCP_REQUEST_DELAY */
986
	debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen);
987
	net_send_packet(net_tx_packet, pktlen);
wdenk's avatar
wdenk committed
988 989 990 991 992
}

/*
 *	Handle DHCP received packets.
 */
993 994
static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
			 unsigned src, unsigned len)
wdenk's avatar
wdenk committed
995
{
996
	struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
997

998
	debug("DHCPHandler: got packet: (src=%d, dst=%d, len=%d) state: %d\n",
999
	      src, dest, len, dhcp_state);
wdenk's avatar
wdenk committed
1000

1001
	/* Filter out pkts we don't want */
1002
	if (check_reply_packet(pkt, dest, src, len))
wdenk's avatar
wdenk committed
1003 1004
		return;

1005 1006
	debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: "
	      "%d\n", src, dest, len, dhcp_state);
wdenk's avatar
wdenk committed
1007

1008 1009 1010
	if (net_read_ip(&bp->bp_yiaddr).s_addr == 0)
		return;

wdenk's avatar
wdenk committed
1011 1012 1013 1014
	switch (dhcp_state) {
	case SELECTING:
		/*
		 * Wait an appropriate time for any potential DHCPOFFER packets
1015 1016 1017
		 * 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
1018
		 */
1019
		debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file);
1020
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
1021
		if (strncmp(bp->bp_file,
1022
			    CONFIG_SYS_BOOTFILE_PREFIX,
1023
			    strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
1024
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
1025
			dhcp_packet_process_options(bp);
wdenk's avatar
wdenk committed
1026

1027
			debug("TRANSITIONING TO REQUESTING STATE\n");
wdenk's avatar
wdenk committed
1028
			dhcp_state = REQUESTING;
1029

1030
			net_set_timeout_handler(5000, bootp_timeout_handler);
1031
			dhcp_send_request_packet(bp);
1032
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
1033
		}
1034
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
wdenk's avatar
wdenk committed
1035 1036 1037 1038

		return;
		break;
	case REQUESTING:
1039
		debug("DHCP State: REQUESTING\n");
wdenk's avatar
wdenk committed
1040

1041
		if (dhcp_message_type((u8 *)bp->bp_vend) == DHCP_ACK) {
1042
			dhcp_packet_process_options(bp);
1043
			/* Store net params from reply */
1044
			store_net_params(bp);
wdenk's avatar
wdenk committed
1045
			dhcp_state = BOUND;
1046
			printf("DHCP client bound to address %pI4 (%lu ms)\n",
1047
			       &net_ip, get_timer(bootp_start));
1048
			net_set_timeout_handler(0, (thand_f *)0);
1049
			bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP,
1050
					    "bootp_stop");
wdenk's avatar
wdenk committed
1051

1052
			net_auto_load();
wdenk's avatar
wdenk committed
1053 1054 1055
			return;
		}
		break;
1056 1057 1058
	case BOUND:
		/* DHCP client bound to address */
		break;
wdenk's avatar
wdenk committed
1059
	default:
1060
		puts("DHCP: INVALID STATE\n");
wdenk's avatar
wdenk committed
1061 1062 1063 1064
		break;
	}
}

1065
void dhcp_request(void)