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

#include <common.h>
#include <command.h>
13
#include <efi_loader.h>
wdenk's avatar
wdenk committed
14
#include <net.h>
15
#include <net/tftp.h>
wdenk's avatar
wdenk committed
16
#include "bootp.h"
17
#include "nfs.h"
18
#ifdef CONFIG_LED_STATUS
wdenk's avatar
wdenk committed
19 20
#include <status_led.h>
#endif
21 22 23
#ifdef CONFIG_BOOTP_RANDOM_DELAY
#include "net_rand.h"
#endif
wdenk's avatar
wdenk committed
24

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

27 28 29 30 31 32 33 34 35 36
/*
 * 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.
 */
37
#ifndef CONFIG_NET_RETRY_COUNT
38
# define TIMEOUT_COUNT	5		/* # of timeouts before giving up */
wdenk's avatar
wdenk committed
39
#else
40
# define TIMEOUT_COUNT	(CONFIG_NET_RETRY_COUNT)
wdenk's avatar
wdenk committed
41
#endif
42
#define TIMEOUT_MS	((3 + (TIMEOUT_COUNT * 5)) * 1000)
wdenk's avatar
wdenk committed
43

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

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

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

55
u32		bootp_ids[CONFIG_BOOTP_ID_CACHE_SIZE];
56
unsigned int	bootp_num_ids;
57
int		bootp_try;
58 59
ulong		bootp_start;
ulong		bootp_timeout;
60 61 62
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
63

64 65
static ulong time_taken_max;

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

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

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
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;
}

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

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

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

	return retval;
}

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

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

166
	debug("net_boot_file_name: %s\n", net_boot_file_name);
wdenk's avatar
wdenk committed
167 168

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

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

188
#if !defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
189

190
static void bootp_process_vendor_field(u8 *ext)
wdenk's avatar
wdenk committed
191
{
192
	int size = *(ext + 1);
wdenk's avatar
wdenk committed
193

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

197
	net_boot_file_expected_size_in_blocks = 0;
wdenk's avatar
wdenk committed
198

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

298
static void bootp_process_vendor(u8 *ext, int size)
wdenk's avatar
wdenk committed
299
{
300
	u8 *end = ext + size;
wdenk's avatar
wdenk committed
301

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

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

			ext += ext[1] + 2;
			if (ext <= end)
312
				bootp_process_vendor_field(opt);
313
		}
wdenk's avatar
wdenk committed
314 315
	}

316
	debug("[BOOTP] Received fields:\n");
317 318
	if (net_netmask.s_addr)
		debug("net_netmask : %pI4\n", &net_netmask);
319

320 321
	if (net_gateway.s_addr)
		debug("net_gateway	: %pI4", &net_gateway);
322

323 324 325
	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
326

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

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

333 334
	if (net_nis_domain[0])
		debug("net_nis_domain : %s\n", net_nis_domain);
335

336
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
337 338
	if (net_ntp_server)
		debug("net_ntp_server : %pI4\n", &net_ntp_server);
339
#endif
340
}
341

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

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

353
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
354

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

	/*
360
	 *	Got a good BOOTP reply.	 Copy the data into our variables.
wdenk's avatar
wdenk committed
361
	 */
362 363
#if defined(CONFIG_LED_STATUS) && defined(CONFIG_LED_STATUS_BOOT_ENABLE)
	status_led_set(CONFIG_LED_STATUS_BOOT, CONFIG_LED_STATUS_OFF);
wdenk's avatar
wdenk committed
364 365
#endif

366
	store_net_params(bp);		/* Store net parameters from reply */
wdenk's avatar
wdenk committed
367 368

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

372
	net_set_timeout_handler(0, (thand_f *)0);
373
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
wdenk's avatar
wdenk committed
374

375
	debug("Got good BOOTP\n");
wdenk's avatar
wdenk committed
376

377
	net_auto_load();
wdenk's avatar
wdenk committed
378
}
379
#endif
wdenk's avatar
wdenk committed
380 381 382 383

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

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

405 406 407 408 409 410 411 412 413
#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)

414 415
static u8 *add_vci(u8 *e)
{
416 417 418
	char *vci = NULL;
	char *env_vci = getenv("bootp_vci");

419
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING)
420
	vci = CONFIG_SPL_NET_VCI_STRING;
421
#elif defined(CONFIG_BOOTP_VCI_STRING)
422
	vci = CONFIG_BOOTP_VCI_STRING;
423 424
#endif

425 426 427 428 429 430
	if (env_vci)
		vci = env_vci;

	if (vci)
		put_vci(e, vci);

431 432 433
	return e;
}

wdenk's avatar
wdenk committed
434 435 436
/*
 *	Initialize BOOTP extension fields in the request.
 */
437
#if defined(CONFIG_CMD_DHCP)
438 439
static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip,
			struct in_addr requested_ip)
wdenk's avatar
wdenk committed
440
{
441 442
	u8 *start = e;
	u8 *cnt;
443
#ifdef CONFIG_LIB_UUID
444 445
	char *uuid;
#endif
446
	int clientarch = -1;
447

448
#if defined(CONFIG_BOOTP_VENDOREX)
449
	u8 *x;
wdenk's avatar
wdenk committed
450
#endif
451
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
Wolfgang Denk's avatar
Wolfgang Denk committed
452
	char *hostname;
453
#endif
wdenk's avatar
wdenk committed
454

455 456 457 458
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
459

460 461 462
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = message_type;
wdenk's avatar
wdenk committed
463

464 465
	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
466 467
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
wdenk's avatar
wdenk committed
468

469 470
	if (server_ip.s_addr) {
		int tmp = ntohl(server_ip.s_addr);
wdenk's avatar
wdenk committed
471

472 473 474 475 476 477 478
		*e++ = 54;	/* ServerID */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
wdenk's avatar
wdenk committed
479

480 481
	if (requested_ip.s_addr) {
		int tmp = ntohl(requested_ip.s_addr);
wdenk's avatar
wdenk committed
482

483 484 485 486 487 488 489
		*e++ = 50;	/* Requested IP */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
490
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
491 492 493
	hostname = getenv("hostname");
	if (hostname) {
		int hostnamelen = strlen(hostname);
494 495 496

		*e++ = 12;	/* Hostname */
		*e++ = hostnamelen;
497
		memcpy(e, hostname, hostnamelen);
498 499
		e += hostnamelen;
	}
500 501
#endif

502
#ifdef CONFIG_BOOTP_PXE_CLIENTARCH
503
	clientarch = CONFIG_BOOTP_PXE_CLIENTARCH;
504 505 506 507 508 509 510 511 512 513 514
#endif

	if (getenv("bootp_arch"))
		clientarch = getenv_ulong("bootp_arch", 16, clientarch);

	if (clientarch > 0) {
		*e++ = 93;	/* Client System Architecture */
		*e++ = 2;
		*e++ = (clientarch >> 8) & 0xff;
		*e++ = clientarch & 0xff;
	}
515 516 517 518 519 520 521

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

522
#ifdef CONFIG_LIB_UUID
523 524 525 526 527 528 529 530
	uuid = getenv("pxeuuid");

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

531
			uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD);
532 533 534 535 536
			e += 16;
		} else {
			printf("Invalid pxeuuid: %s\n", uuid);
		}
	}
537
#endif
538

539
	e = add_vci(e);
540

541
#if defined(CONFIG_BOOTP_VENDOREX)
542 543
	x = dhcp_vendorex_prep(e);
	if (x)
544
		return x - start;
wdenk's avatar
wdenk committed
545 546
#endif

547 548 549
	*e++ = 55;		/* Parameter Request List */
	 cnt = e++;		/* Pointer to count of requested items */
	*cnt = 0;
550
#if defined(CONFIG_BOOTP_SUBNETMASK)
551 552
	*e++  = 1;		/* Subnet Mask */
	*cnt += 1;
wdenk's avatar
wdenk committed
553
#endif
554
#if defined(CONFIG_BOOTP_TIMEOFFSET)
555 556 557
	*e++  = 2;
	*cnt += 1;
#endif
558
#if defined(CONFIG_BOOTP_GATEWAY)
559 560
	*e++  = 3;		/* Router Option */
	*cnt += 1;
wdenk's avatar
wdenk committed
561
#endif
562
#if defined(CONFIG_BOOTP_DNS)
563 564
	*e++  = 6;		/* DNS Server(s) */
	*cnt += 1;
wdenk's avatar
wdenk committed
565
#endif
566
#if defined(CONFIG_BOOTP_HOSTNAME)
567 568
	*e++  = 12;		/* Hostname */
	*cnt += 1;
wdenk's avatar
wdenk committed
569
#endif
570
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
571 572
	*e++  = 13;		/* Boot File Size */
	*cnt += 1;
wdenk's avatar
wdenk committed
573
#endif
574
#if defined(CONFIG_BOOTP_BOOTPATH)
575 576
	*e++  = 17;		/* Boot path */
	*cnt += 1;
wdenk's avatar
wdenk committed
577
#endif
578
#if defined(CONFIG_BOOTP_NISDOMAIN)
579 580
	*e++  = 40;		/* NIS Domain name request */
	*cnt += 1;
581
#endif
582
#if defined(CONFIG_BOOTP_NTPSERVER)
583 584
	*e++  = 42;
	*cnt += 1;
wdenk's avatar
wdenk committed
585
#endif
586 587 588 589
	/* no options, so back up to avoid sending an empty request list */
	if (*cnt == 0)
		e -= 2;

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

592
	/* Pad to minimal length */
wdenk's avatar
wdenk committed
593
#ifdef	CONFIG_DHCP_MIN_EXT_LEN
594
	while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN)
595
		*e++ = 0;
wdenk's avatar
wdenk committed
596 597
#endif

598
	return e - start;
wdenk's avatar
wdenk committed
599 600
}

601
#else
wdenk's avatar
wdenk committed
602
/*
603
 * Warning: no field size check - change CONFIG_BOOTP_* at your own risk!
wdenk's avatar
wdenk committed
604
 */
605
static int bootp_extended(u8 *e)
wdenk's avatar
wdenk committed
606
{
607
	u8 *start = e;
wdenk's avatar
wdenk committed
608

609 610 611 612
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
613

614
#if defined(CONFIG_CMD_DHCP)
615 616 617 618 619 620
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = DHCP_DISCOVER;

	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
621 622
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
623
#endif
wdenk's avatar
wdenk committed
624

625
	add_vci(e);
626

627
#if defined(CONFIG_BOOTP_SUBNETMASK)
628 629 630
	*e++ = 1;		/* Subnet mask request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
631 632
#endif

633
#if defined(CONFIG_BOOTP_GATEWAY)
634 635 636
	*e++ = 3;		/* Default gateway request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
637 638
#endif

639
#if defined(CONFIG_BOOTP_DNS)
640 641 642
	*e++ = 6;		/* Domain Name Server */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
643 644
#endif

645
#if defined(CONFIG_BOOTP_HOSTNAME)
646 647 648
	*e++ = 12;		/* Host name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
649 650
#endif

651
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
652 653 654
	*e++ = 13;		/* Boot file size */
	*e++ = 2;
	e   += 2;
wdenk's avatar
wdenk committed
655 656
#endif

657
#if defined(CONFIG_BOOTP_BOOTPATH)
658 659 660
	*e++ = 17;		/* Boot path */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
661 662
#endif

663
#if defined(CONFIG_BOOTP_NISDOMAIN)
664 665 666
	*e++ = 40;		/* NIS Domain name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
667
#endif
668 669 670 671 672
#if defined(CONFIG_BOOTP_NTPSERVER)
	*e++ = 42;
	*e++ = 4;
	e   += 4;
#endif
wdenk's avatar
wdenk committed
673

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

676 677 678 679 680 681 682 683 684
	/*
	 * If nothing in list, remove it altogether. Some DHCP servers get
	 * upset by this minor faux pas and do not respond at all.
	 */
	if (e == start + 3) {
		printf("*** Warning: no DHCP options requested\n");
		e -= 3;
	}

685
	return e - start;
wdenk's avatar
wdenk committed
686
}
687
#endif
wdenk's avatar
wdenk committed
688

689
void bootp_reset(void)
690
{
691
	bootp_num_ids = 0;
692
	bootp_try = 0;
693
	bootp_start = get_timer(0);
694
	bootp_timeout = 250;
695 696
}

697
void bootp_request(void)
wdenk's avatar
wdenk committed
698
{
699
	uchar *pkt, *iphdr;
700
	struct bootp_hdr *bp;
701 702
	int extlen, pktlen, iplen;
	int eth_hdr_size;
703
#ifdef CONFIG_BOOTP_RANDOM_DELAY
Pavel Machek's avatar
Pavel Machek committed
704
	ulong rand_ms;
705
#endif
706
	u32 bootp_id;
707 708
	struct in_addr zero_ip;
	struct in_addr bcast_ip;
709
	char *ep;  /* Environment pointer */
wdenk's avatar
wdenk committed
710

711
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
712
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
713 714 715
	dhcp_state = INIT;
#endif

716 717 718 719 720 721
	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
722
#ifdef CONFIG_BOOTP_RANDOM_DELAY		/* Random BOOTP delay */
723
	if (bootp_try == 0)
724
		srand_mac();
wdenk's avatar
wdenk committed
725

726 727
	if (bootp_try <= 2)	/* Start with max 1024 * 1ms */
		rand_ms = rand() >> (22 - bootp_try);
728 729
	else		/* After 3rd BOOTP request max 8192 * 1ms */
		rand_ms = rand() >> 19;
wdenk's avatar
wdenk committed
730

731
	printf("Random delay: %ld ms...\n", rand_ms);
Pavel Machek's avatar
Pavel Machek committed
732
	mdelay(rand_ms);
733

wdenk's avatar
wdenk committed
734 735
#endif	/* CONFIG_BOOTP_RANDOM_DELAY */

736
	printf("BOOTP broadcast %d\n", ++bootp_try);
737
	pkt = net_tx_packet;
738
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
739

740
	eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
741
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
742 743

	/*
744 745 746 747
	 * 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
748 749
	 * C. Hallinan, DS4.COM, Inc.
	 */
750
	/* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC,
751
		sizeof (struct bootp_hdr)); */
752
	iphdr = pkt;	/* We need this later for net_set_udp_header() */
753
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
754

755
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
756 757 758 759
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
760 761 762 763 764
	/*
	 * according to RFC1542, should be 0 on first request, secs since
	 * first request otherwise
	 */
	bp->bp_secs = htons(get_timer(bootp_start) / 1000);
765 766 767 768 769
	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);
770
	memcpy(bp->bp_chaddr, net_ethaddr, 6);
771
	copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
wdenk's avatar
wdenk committed
772 773

	/* Request additional information from the BOOTP/DHCP server */
774
#if defined(CONFIG_CMD_DHCP)
775 776
	extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_DISCOVER, zero_ip,
			       zero_ip);
wdenk's avatar
wdenk committed
777
#else
778
	extlen = bootp_extended((u8 *)bp->bp_vend);
779
#endif
wdenk's avatar
wdenk committed
780 781 782

	/*
	 *	Bootp ID is the lower 4 bytes of our ethernet address
783
	 *	plus the current time in ms.
wdenk's avatar
wdenk committed
784
	 */
785 786 787 788
	bootp_id = ((u32)net_ethaddr[2] << 24)
		| ((u32)net_ethaddr[3] << 16)
		| ((u32)net_ethaddr[4] << 8)
		| (u32)net_ethaddr[5];
789 790 791
	bootp_id += get_timer(0);
	bootp_id = htonl(bootp_id);
	bootp_add_id(bootp_id);
792
	net_copy_u32(&bp->bp_id, &bootp_id);
wdenk's avatar
wdenk committed
793 794 795 796 797

	/*
	 * Calculate proper packet lengths taking into account the
	 * variable size of the options field
	 */
798 799
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
800 801
	bcast_ip.s_addr = 0xFFFFFFFFL;
	net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
802
	net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
wdenk's avatar
wdenk committed
803

804
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
805
	dhcp_state = SELECTING;
806
	net_set_udp_handler(dhcp_handler);
wdenk's avatar
wdenk committed
807
#else
808
	net_set_udp_handler(bootp_handler);
809
#endif
810
	net_send_packet(net_tx_packet, pktlen);
wdenk's avatar
wdenk committed
811 812
}

813
#if defined(CONFIG_CMD_DHCP)
814
static void dhcp_process_options(uchar *popt, uchar *end)
wdenk's avatar
wdenk committed
815 816
{
	int oplen, size;
817 818 819
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
	int *to_ptr;
#endif
wdenk's avatar
wdenk committed
820

821
	while (popt < end && *popt != 0xff) {
wdenk's avatar
wdenk committed
822
		oplen = *(popt + 1);
823
		switch (*popt) {
824 825 826
		case 0:
			oplen = -1; /* Pad omits len byte */
			break;
827
		case 1:
828
			net_copy_ip(&net_netmask, (popt + 2));
829
			break;
830
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
831
		case 2:		/* Time offset	*/
832
			to_ptr = &net_ntp_time_offset;
833
			net_copy_u32((u32 *)to_ptr, (u32 *)(popt + 2));
834
			net_ntp_time_offset = ntohl(net_ntp_time_offset);
835 836
			break;
#endif
837
		case 3:
838
			net_copy_ip(&net_gateway, (popt + 2));
839 840
			break;
		case 6:
841
			net_copy_ip(&net_dns_server, (popt + 2));
842
#if defined(CONFIG_BOOTP_DNS2)
843
			if (*(popt + 1) > 4)
844
				net_copy_ip(&net_dns_server2, (popt + 2 + 4));
845
#endif
846 847
			break;
		case 12:
848
			size = truncate_sz("Host Name",
849 850 851
				sizeof(net_hostname), oplen);
			memcpy(&net_hostname, popt + 2, size);
			net_hostname[size] = 0;
852 853 854 855
			break;
		case 15:	/* Ignore Domain Name Option */
			break;
		case 17:
856
			size = truncate_sz("Root Path",
857 858 859
				sizeof(net_root_path), oplen);
			memcpy(&net_root_path, popt + 2, size);
			net_root_path[size] = 0;
860
			break;
861 862
		case 28:	/* Ignore Broadcast Address Option */
			break;
863
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
864
		case 42:	/* NTP server IP */
865
			net_copy_ip(&net_ntp_server, (popt + 2));
866 867
			break;
#endif
868
		case 51:
869
			net_copy_u32(&dhcp_leasetime, (u32 *)(popt + 2));
870
			break;
871 872 873
		case 52:
			dhcp_option_overload = popt[2];
			break;
874 875 876
		case 53:	/* Ignore Message Type Option */
			break;
		case 54:
877
			net_copy_ip(&dhcp_server_ip, (popt + 2));
878 879 880 881 882
			break;
		case 58:	/* Ignore Renewal Time Option */
			break;
		case 59:	/* Ignore Rebinding Time Option */
			break;
883 884
		case 66:	/* Ignore TFTP server name */
			break;
885 886 887 888 889
		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;
890
			break;
891
		default:
892
#if defined(CONFIG_BOOTP_VENDOREX)
893
			if (dhcp_vendorex_proc(popt))
wdenk's avatar
wdenk committed
894
				break;
wdenk's avatar
wdenk committed
895
#endif
896
			printf("*** Unhandled DHCP Option in OFFER/ACK:"
897
			       " %d\n", *popt);
898
			break;
wdenk's avatar
wdenk committed
899 900 901 902 903
		}
		popt += oplen + 2;	/* Process next option */
	}
}

904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
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);
	}
}

933
static int dhcp_message_type(unsigned char *popt)
wdenk's avatar
wdenk committed
934
{
935
	if (net_read_u32((u32 *)popt) != htonl(BOOTP_VENDOR_MAGIC))
wdenk's avatar
wdenk committed
936 937 938
		return -1;

	popt += 4;
939 940
	while (*popt != 0xff) {
		if (*popt == 53)	/* DHCP Message Type */
wdenk's avatar
wdenk committed
941
			return *(popt + 2);
942 943 944 945 946 947 948
		if (*popt == 0)	{
			/* Pad */
			popt += 1;
		} else {
			/* Scan through all options */
			popt += *(popt + 1) + 2;
		}
wdenk's avatar
wdenk committed
949 950 951 952
	}
	return -1;
}

953
static void dhcp_send_request_packet(struct bootp_hdr *bp_offer)
wdenk's avatar
wdenk committed
954
{
955
	uchar *pkt, *iphdr;
956
	struct bootp_hdr *bp;
wdenk's avatar
wdenk committed
957
	int pktlen, iplen, extlen;
958
	int eth_hdr_size;
959 960 961
	struct in_addr offered_ip;
	struct in_addr zero_ip;
	struct in_addr bcast_ip;
wdenk's avatar
wdenk committed
962

963
	debug("dhcp_send_request_packet: Sending DHCPREQUEST\n");
964
	pkt = net_tx_packet;
965
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
966

967
	eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
968
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
969

970
	iphdr = pkt;	/* We'll need this later to set proper pkt size */
971
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
972

973
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
974 975 976 977
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
978
	bp->bp_secs = htons(get_timer(bootp_start) / 1000);
979 980
	/* 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
981

Wolfgang Denk's avatar
Wolfgang Denk committed
982
	/*
983 984 985
	 * RFC3046 requires Relay Agents to discard packets with
	 * nonzero and offered giaddr
	 */
986 987
	zero_ip.s_addr = 0;
	net_write_ip(&bp->bp_giaddr, zero_ip);
988

989
	memcpy(bp->bp_chaddr, net_ethaddr, 6);
990
	copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
wdenk's avatar
wdenk committed
991 992 993 994 995

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

996
	net_copy_u32(&bp->bp_id, &bp_offer->bp_id);
wdenk's avatar
wdenk committed
997 998 999 1000

	/*
	 * Copy options from OFFER packet if present
	 */
Justin Flammia's avatar
Justin Flammia committed
1001 1002

	/* Copy offered IP into the parameters request list */
1003 1004 1005
	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
1006

1007 1008
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
1009 1010
	bcast_ip.s_addr = 0xFFFFFFFFL;
	net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
wdenk's avatar
wdenk committed
1011

1012 1013 1014
#ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY
	udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY);
#endif	/* CONFIG_BOOTP_DHCP_REQUEST_DELAY */
1015
	debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen);
1016
	net_send_packet(net_tx_packet, pktlen);
wdenk's avatar
wdenk committed
1017 1018 1019 1020 1021
}

/*
 *	Handle DHCP received packets.
 */
1022 1023
static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
			 unsigned src, unsigned len)
wdenk's avatar
wdenk committed
1024
{
1025
	struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
1026

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

1030
	/* Filter out pkts we don't want */
1031
	if (check_reply_packet(pkt, dest, src, len))
wdenk's avatar
wdenk committed
1032 1033
		return;

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

1037 1038 1039
	if (net_read_ip(&bp->bp_yiaddr).s_addr == 0)
		return;

wdenk's avatar
wdenk committed
1040 1041 1042 1043
	switch (dhcp_state) {
	case SELECTING:
		/*
		 * Wait an appropriate time for any potential DHCPOFFER packets
1044 1045 1046
		 * 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
1047
		 */
1048
		debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file);
1049
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
1050
		if (strncmp(bp->bp_file,
1051
			    CONFIG_SYS_BOOTFILE_PREFIX,
1052
			    strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
1053
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
1054
			dhcp_packet_process_options(bp);
1055
			efi_net_set_dhcp_ack(pkt, len);
wdenk's avatar
wdenk committed
1056

1057
			debug("TRANSITIONING TO REQUESTING STATE\n");
wdenk's avatar
wdenk committed
1058
			dhcp_state = REQUESTING;
1059

1060
			net_set_timeout_handler(5000, bootp_timeout_handler);
1061
			dhcp_send_request_packet(bp);
1062
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
1063
		}
1064
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
wdenk's avatar
wdenk committed
1065