bootp.c 24.9 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
#if defined(CONFIG_CMD_DHCP)
Kim Phillips's avatar
Kim Phillips committed
64
static dhcp_state_t dhcp_state = INIT;
65
static u32 dhcp_leasetime;
66 67 68
static struct in_addr dhcp_server_ip;
static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
			unsigned src, unsigned len);
wdenk's avatar
wdenk committed
69 70

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

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
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;
}

112
static int check_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len)
wdenk's avatar
wdenk committed
113
{
114
	struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
115 116 117 118
	int retval = 0;

	if (dest != PORT_BOOTPC || src != PORT_BOOTPS)
		retval = -1;
119
	else if (len < sizeof(struct bootp_hdr) - OPT_FIELD_SIZE)
wdenk's avatar
wdenk committed
120 121
		retval = -2;
	else if (bp->bp_op != OP_BOOTREQUEST &&
122 123 124 125
			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
126 127 128 129 130
		retval = -3;
	else if (bp->bp_htype != HWT_ETHER)
		retval = -4;
	else if (bp->bp_hlen != HWL_ETHER)
		retval = -5;
131
	else if (!bootp_match_id(net_read_u32(&bp->bp_id)))
wdenk's avatar
wdenk committed
132 133
		retval = -6;

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

	return retval;
}

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

147 148 149
	net_copy_ip(&tmp_ip, &bp->bp_siaddr);
	if (tmp_ip.s_addr != 0)
		net_copy_ip(&net_server_ip, &bp->bp_siaddr);
150 151
	memcpy(net_server_ethaddr,
	       ((struct ethernet_hdr *)net_rx_packet)->et_src, 6);
152
	if (strlen(bp->bp_file) > 0)
153 154
		copy_filename(net_boot_file_name, bp->bp_file,
			      sizeof(net_boot_file_name));
wdenk's avatar
wdenk committed
155

156
	debug("net_boot_file_name: %s\n", net_boot_file_name);
wdenk's avatar
wdenk committed
157 158

	/* Propagate to environment:
wdenk's avatar
wdenk committed
159
	 * don't delete exising entry when BOOTP / DHCP reply does
wdenk's avatar
wdenk committed
160 161
	 * not contain a new value
	 */
162 163
	if (*net_boot_file_name)
		setenv("bootfile", net_boot_file_name);
164
#endif
165
	net_copy_ip(&net_ip, &bp->bp_yiaddr);
wdenk's avatar
wdenk committed
166 167
}

168
static int truncate_sz(const char *name, int maxlen, int curlen)
wdenk's avatar
wdenk committed
169 170
{
	if (curlen >= maxlen) {
171 172
		printf("*** WARNING: %s is too long (%d - max: %d)"
			" - truncated\n", name, curlen, maxlen);
wdenk's avatar
wdenk committed
173 174
		curlen = maxlen - 1;
	}
175
	return curlen;
wdenk's avatar
wdenk committed
176 177
}

178
#if !defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
179

180
static void bootp_process_vendor_field(u8 *ext)
wdenk's avatar
wdenk committed
181
{
182
	int size = *(ext + 1);
wdenk's avatar
wdenk committed
183

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

187
	net_boot_file_expected_size_in_blocks = 0;
wdenk's avatar
wdenk committed
188

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

288
static void bootp_process_vendor(u8 *ext, int size)
wdenk's avatar
wdenk committed
289
{
290
	u8 *end = ext + size;
wdenk's avatar
wdenk committed
291

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

294 295 296 297 298 299 300 301
	while ((ext < end) && (*ext != 0xff)) {
		if (*ext == 0) {
			ext++;
		} else {
			u8 *opt = ext;

			ext += ext[1] + 2;
			if (ext <= end)
302
				bootp_process_vendor_field(opt);
303
		}
wdenk's avatar
wdenk committed
304 305
	}

306
	debug("[BOOTP] Received fields:\n");
307 308
	if (net_netmask.s_addr)
		debug("net_netmask : %pI4\n", &net_netmask);
309

310 311
	if (net_gateway.s_addr)
		debug("net_gateway	: %pI4", &net_gateway);
312

313 314 315
	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
316

317 318
	if (net_hostname[0])
		debug("net_hostname  : %s\n", net_hostname);
319

320 321
	if (net_root_path[0])
		debug("net_root_path  : %s\n", net_root_path);
322

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

326
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
327 328
	if (net_ntp_server)
		debug("net_ntp_server : %pI4\n", &net_ntp_server);
329
#endif
330
}
331

wdenk's avatar
wdenk committed
332 333 334
/*
 *	Handle a BOOTP received packet.
 */
335 336
static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
			  unsigned src, unsigned len)
wdenk's avatar
wdenk committed
337
{
338
	struct bootp_hdr *bp;
wdenk's avatar
wdenk committed
339

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

343
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
344

345
	/* Filter out pkts we don't want */
346
	if (check_packet(pkt, dest, src, len))
wdenk's avatar
wdenk committed
347 348 349
		return;

	/*
350
	 *	Got a good BOOTP reply.	 Copy the data into our variables.
wdenk's avatar
wdenk committed
351 352
	 */
#ifdef CONFIG_STATUS_LED
353
	status_led_set(STATUS_LED_BOOT, STATUS_LED_OFF);
wdenk's avatar
wdenk committed
354 355
#endif

356
	store_net_params(bp);		/* Store net parameters from reply */
wdenk's avatar
wdenk committed
357 358

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

362
	net_set_timeout_handler(0, (thand_f *)0);
363
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
wdenk's avatar
wdenk committed
364

365
	debug("Got good BOOTP\n");
wdenk's avatar
wdenk committed
366

367
	net_auto_load();
wdenk's avatar
wdenk committed
368
}
369
#endif
wdenk's avatar
wdenk committed
370 371 372 373

/*
 *	Timeout on BOOTP/DHCP request.
 */
374
static void bootp_timeout_handler(void)
wdenk's avatar
wdenk committed
375
{
376 377 378
	ulong time_taken = get_timer(bootp_start);

	if (time_taken >= TIMEOUT_MS) {
379
#ifdef CONFIG_BOOTP_MAY_FAIL
380
		puts("\nRetry time exceeded\n");
381
		net_set_state(NETLOOP_FAIL);
382
#else
383
		puts("\nRetry time exceeded; starting again\n");
384
		net_start_again();
385
#endif
wdenk's avatar
wdenk committed
386
	} else {
387
		bootp_timeout *= 2;
388 389
		if (bootp_timeout > 2000)
			bootp_timeout = 2000;
390
		net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
391
		bootp_request();
wdenk's avatar
wdenk committed
392 393 394
	}
}

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

418
#if defined(CONFIG_BOOTP_VENDOREX)
419
	u8 *x;
wdenk's avatar
wdenk committed
420
#endif
421
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
Wolfgang Denk's avatar
Wolfgang Denk committed
422
	char *hostname;
423
#endif
wdenk's avatar
wdenk committed
424

425 426 427 428
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
429

430 431 432
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = message_type;
wdenk's avatar
wdenk committed
433

434 435
	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
436 437
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
wdenk's avatar
wdenk committed
438

439 440
	if (server_ip.s_addr) {
		int tmp = ntohl(server_ip.s_addr);
wdenk's avatar
wdenk committed
441

442 443 444 445 446 447 448
		*e++ = 54;	/* ServerID */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
wdenk's avatar
wdenk committed
449

450 451
	if (requested_ip.s_addr) {
		int tmp = ntohl(requested_ip.s_addr);
wdenk's avatar
wdenk committed
452

453 454 455 456 457 458 459
		*e++ = 50;	/* Requested IP */
		*e++ = 4;
		*e++ = tmp >> 24;
		*e++ = tmp >> 16;
		*e++ = tmp >> 8;
		*e++ = tmp & 0xff;
	}
460
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
461 462 463
	hostname = getenv("hostname");
	if (hostname) {
		int hostnamelen = strlen(hostname);
464 465 466

		*e++ = 12;	/* Hostname */
		*e++ = hostnamelen;
467
		memcpy(e, hostname, hostnamelen);
468 469
		e += hostnamelen;
	}
470 471
#endif

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
#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 */

493
			uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD);
494 495 496 497 498
			e += 16;
		} else {
			printf("Invalid pxeuuid: %s\n", uuid);
		}
	}
499
#endif
500

501 502
#ifdef CONFIG_BOOTP_VCI_STRING
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
503 504
#endif

505
#if defined(CONFIG_BOOTP_VENDOREX)
506 507
	x = dhcp_vendorex_prep(e);
	if (x)
508
		return x - start;
wdenk's avatar
wdenk committed
509 510
#endif

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

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

556
	/* Pad to minimal length */
wdenk's avatar
wdenk committed
557
#ifdef	CONFIG_DHCP_MIN_EXT_LEN
558
	while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN)
559
		*e++ = 0;
wdenk's avatar
wdenk committed
560 561
#endif

562
	return e - start;
wdenk's avatar
wdenk committed
563 564
}

565
#else
wdenk's avatar
wdenk committed
566
/*
567
 * Warning: no field size check - change CONFIG_BOOTP_* at your own risk!
wdenk's avatar
wdenk committed
568
 */
569
static int bootp_extended(u8 *e)
wdenk's avatar
wdenk committed
570
{
571
	u8 *start = e;
wdenk's avatar
wdenk committed
572

573 574 575 576
	*e++ = 99;		/* RFC1048 Magic Cookie */
	*e++ = 130;
	*e++ = 83;
	*e++ = 99;
wdenk's avatar
wdenk committed
577

578
#if defined(CONFIG_CMD_DHCP)
579 580 581 582 583 584
	*e++ = 53;		/* DHCP Message Type */
	*e++ = 1;
	*e++ = DHCP_DISCOVER;

	*e++ = 57;		/* Maximum DHCP Message Size */
	*e++ = 2;
585 586
	*e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16;
	*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
587
#endif
wdenk's avatar
wdenk committed
588

589 590 591 592 593
#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
594 595
	put_vci(e, CONFIG_BOOTP_VCI_STRING);
#endif
596
#endif
597

598
#if defined(CONFIG_BOOTP_SUBNETMASK)
599 600 601
	*e++ = 1;		/* Subnet mask request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
602 603
#endif

604
#if defined(CONFIG_BOOTP_GATEWAY)
605 606 607
	*e++ = 3;		/* Default gateway request */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
608 609
#endif

610
#if defined(CONFIG_BOOTP_DNS)
611 612 613
	*e++ = 6;		/* Domain Name Server */
	*e++ = 4;
	e   += 4;
wdenk's avatar
wdenk committed
614 615
#endif

616
#if defined(CONFIG_BOOTP_HOSTNAME)
617 618 619
	*e++ = 12;		/* Host name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
620 621
#endif

622
#if defined(CONFIG_BOOTP_BOOTFILESIZE)
623 624 625
	*e++ = 13;		/* Boot file size */
	*e++ = 2;
	e   += 2;
wdenk's avatar
wdenk committed
626 627
#endif

628
#if defined(CONFIG_BOOTP_BOOTPATH)
629 630 631
	*e++ = 17;		/* Boot path */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
632 633
#endif

634
#if defined(CONFIG_BOOTP_NISDOMAIN)
635 636 637
	*e++ = 40;		/* NIS Domain name request */
	*e++ = 32;
	e   += 32;
wdenk's avatar
wdenk committed
638
#endif
639 640 641 642 643
#if defined(CONFIG_BOOTP_NTPSERVER)
	*e++ = 42;
	*e++ = 4;
	e   += 4;
#endif
wdenk's avatar
wdenk committed
644

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

647
	return e - start;
wdenk's avatar
wdenk committed
648
}
649
#endif
wdenk's avatar
wdenk committed
650

651
void bootp_reset(void)
652
{
653
	bootp_num_ids = 0;
654
	bootp_try = 0;
655
	bootp_start = get_timer(0);
656
	bootp_timeout = 250;
657 658
}

659
void bootp_request(void)
wdenk's avatar
wdenk committed
660
{
661
	uchar *pkt, *iphdr;
662
	struct bootp_hdr *bp;
663 664
	int extlen, pktlen, iplen;
	int eth_hdr_size;
665
#ifdef CONFIG_BOOTP_RANDOM_DELAY
Pavel Machek's avatar
Pavel Machek committed
666
	ulong rand_ms;
667
#endif
668
	u32 bootp_id;
669 670
	struct in_addr zero_ip;
	struct in_addr bcast_ip;
wdenk's avatar
wdenk committed
671

672
	bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
673
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
674 675 676 677
	dhcp_state = INIT;
#endif

#ifdef CONFIG_BOOTP_RANDOM_DELAY		/* Random BOOTP delay */
678
	if (bootp_try == 0)
679
		srand_mac();
wdenk's avatar
wdenk committed
680

681 682
	if (bootp_try <= 2)	/* Start with max 1024 * 1ms */
		rand_ms = rand() >> (22 - bootp_try);
683 684
	else		/* After 3rd BOOTP request max 8192 * 1ms */
		rand_ms = rand() >> 19;
wdenk's avatar
wdenk committed
685

686
	printf("Random delay: %ld ms...\n", rand_ms);
Pavel Machek's avatar
Pavel Machek committed
687
	mdelay(rand_ms);
688

wdenk's avatar
wdenk committed
689 690
#endif	/* CONFIG_BOOTP_RANDOM_DELAY */

691
	printf("BOOTP broadcast %d\n", ++bootp_try);
692
	pkt = net_tx_packet;
693
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
694

695
	eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
696
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
697 698

	/*
699 700 701 702
	 * 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
703 704
	 * C. Hallinan, DS4.COM, Inc.
	 */
705
	/* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC,
706
		sizeof (struct bootp_hdr)); */
707
	iphdr = pkt;	/* We need this later for net_set_udp_header() */
708
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
709

710
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
711 712 713 714
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
715
	bp->bp_secs = htons(get_timer(0) / 1000);
716 717 718 719 720
	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);
721
	memcpy(bp->bp_chaddr, net_ethaddr, 6);
722
	copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
wdenk's avatar
wdenk committed
723 724

	/* Request additional information from the BOOTP/DHCP server */
725
#if defined(CONFIG_CMD_DHCP)
726 727
	extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_DISCOVER, zero_ip,
			       zero_ip);
wdenk's avatar
wdenk committed
728
#else
729
	extlen = bootp_extended((u8 *)bp->bp_vend);
730
#endif
wdenk's avatar
wdenk committed
731 732 733

	/*
	 *	Bootp ID is the lower 4 bytes of our ethernet address
734
	 *	plus the current time in ms.
wdenk's avatar
wdenk committed
735
	 */
736 737 738 739
	bootp_id = ((u32)net_ethaddr[2] << 24)
		| ((u32)net_ethaddr[3] << 16)
		| ((u32)net_ethaddr[4] << 8)
		| (u32)net_ethaddr[5];
740 741 742
	bootp_id += get_timer(0);
	bootp_id = htonl(bootp_id);
	bootp_add_id(bootp_id);
743
	net_copy_u32(&bp->bp_id, &bootp_id);
wdenk's avatar
wdenk committed
744 745 746 747 748

	/*
	 * Calculate proper packet lengths taking into account the
	 * variable size of the options field
	 */
749 750
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
751 752
	bcast_ip.s_addr = 0xFFFFFFFFL;
	net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
753
	net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
wdenk's avatar
wdenk committed
754

755
#if defined(CONFIG_CMD_DHCP)
wdenk's avatar
wdenk committed
756
	dhcp_state = SELECTING;
757
	net_set_udp_handler(dhcp_handler);
wdenk's avatar
wdenk committed
758
#else
759
	net_set_udp_handler(bootp_handler);
760
#endif
761
	net_send_packet(net_tx_packet, pktlen);
wdenk's avatar
wdenk committed
762 763
}

764
#if defined(CONFIG_CMD_DHCP)
765
static void dhcp_process_options(uchar *popt, struct bootp_hdr *bp)
wdenk's avatar
wdenk committed
766
{
767
	uchar *end = popt + BOOTP_HDR_SIZE;
wdenk's avatar
wdenk committed
768
	int oplen, size;
769 770 771
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
	int *to_ptr;
#endif
wdenk's avatar
wdenk committed
772

773
	while (popt < end && *popt != 0xff) {
wdenk's avatar
wdenk committed
774
		oplen = *(popt + 1);
775 776
		switch (*popt) {
		case 1:
777
			net_copy_ip(&net_netmask, (popt + 2));
778
			break;
779
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
780
		case 2:		/* Time offset	*/
781
			to_ptr = &net_ntp_time_offset;
782
			net_copy_u32((u32 *)to_ptr, (u32 *)(popt + 2));
783
			net_ntp_time_offset = ntohl(net_ntp_time_offset);
784 785
			break;
#endif
786
		case 3:
787
			net_copy_ip(&net_gateway, (popt + 2));
788 789
			break;
		case 6:
790
			net_copy_ip(&net_dns_server, (popt + 2));
791
#if defined(CONFIG_BOOTP_DNS2)
792
			if (*(popt + 1) > 4)
793
				net_copy_ip(&net_dns_server2, (popt + 2 + 4));
794
#endif
795 796
			break;
		case 12:
797
			size = truncate_sz("Host Name",
798 799 800
				sizeof(net_hostname), oplen);
			memcpy(&net_hostname, popt + 2, size);
			net_hostname[size] = 0;
801 802 803 804
			break;
		case 15:	/* Ignore Domain Name Option */
			break;
		case 17:
805
			size = truncate_sz("Root Path",
806 807 808
				sizeof(net_root_path), oplen);
			memcpy(&net_root_path, popt + 2, size);
			net_root_path[size] = 0;
809
			break;
810 811
		case 28:	/* Ignore Broadcast Address Option */
			break;
812
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
813
		case 42:	/* NTP server IP */
814
			net_copy_ip(&net_ntp_server, (popt + 2));
815 816
			break;
#endif
817
		case 51:
818
			net_copy_u32(&dhcp_leasetime, (u32 *)(popt + 2));
819 820 821 822
			break;
		case 53:	/* Ignore Message Type Option */
			break;
		case 54:
823
			net_copy_ip(&dhcp_server_ip, (popt + 2));
824 825 826 827 828
			break;
		case 58:	/* Ignore Renewal Time Option */
			break;
		case 59:	/* Ignore Rebinding Time Option */
			break;
829 830 831 832 833 834 835 836 837
		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
			 */
838
			size = truncate_sz("Opt Boot File",
839 840 841 842 843 844 845 846 847 848 849 850 851
					    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 "
852
				       "optional boot file\n");
853 854 855 856
				memcpy(bp->bp_file, popt + 2, size);
				bp->bp_file[size] = '\0';
			}
			break;
857
		default:
858
#if defined(CONFIG_BOOTP_VENDOREX)
859
			if (dhcp_vendorex_proc(popt))
wdenk's avatar
wdenk committed
860
				break;
wdenk's avatar
wdenk committed
861
#endif
862
			printf("*** Unhandled DHCP Option in OFFER/ACK:"
863
			       " %d\n", *popt);
864
			break;
wdenk's avatar
wdenk committed
865 866 867 868 869
		}
		popt += oplen + 2;	/* Process next option */
	}
}

870
static int dhcp_message_type(unsigned char *popt)
wdenk's avatar
wdenk committed
871
{
872
	if (net_read_u32((u32 *)popt) != htonl(BOOTP_VENDOR_MAGIC))
wdenk's avatar
wdenk committed
873 874 875
		return -1;

	popt += 4;
876 877
	while (*popt != 0xff) {
		if (*popt == 53)	/* DHCP Message Type */
wdenk's avatar
wdenk committed
878 879 880 881 882 883
			return *(popt + 2);
		popt += *(popt + 1) + 2;	/* Scan through all options */
	}
	return -1;
}

884
static void dhcp_send_request_packet(struct bootp_hdr *bp_offer)
wdenk's avatar
wdenk committed
885
{
886
	uchar *pkt, *iphdr;
887
	struct bootp_hdr *bp;
wdenk's avatar
wdenk committed
888
	int pktlen, iplen, extlen;
889
	int eth_hdr_size;
890 891 892
	struct in_addr offered_ip;
	struct in_addr zero_ip;
	struct in_addr bcast_ip;
wdenk's avatar
wdenk committed
893

894
	debug("dhcp_send_request_packet: Sending DHCPREQUEST\n");
895
	pkt = net_tx_packet;
896
	memset((void *)pkt, 0, PKTSIZE);
wdenk's avatar
wdenk committed
897

898
	eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
899
	pkt += eth_hdr_size;
wdenk's avatar
wdenk committed
900

901
	iphdr = pkt;	/* We'll need this later to set proper pkt size */
902
	pkt += IP_UDP_HDR_SIZE;
wdenk's avatar
wdenk committed
903

904
	bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
905 906 907 908
	bp->bp_op = OP_BOOTREQUEST;
	bp->bp_htype = HWT_ETHER;
	bp->bp_hlen = HWL_ETHER;
	bp->bp_hops = 0;
909
	bp->bp_secs = htons(get_timer(0) / 1000);
910 911
	/* 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
912

Wolfgang Denk's avatar
Wolfgang Denk committed
913
	/*
914 915 916
	 * RFC3046 requires Relay Agents to discard packets with
	 * nonzero and offered giaddr
	 */
917 918
	zero_ip.s_addr = 0;
	net_write_ip(&bp->bp_giaddr, zero_ip);
919

920
	memcpy(bp->bp_chaddr, net_ethaddr, 6);
wdenk's avatar
wdenk committed
921 922 923 924 925

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

926
	net_copy_u32(&bp->bp_id, &bp_offer->bp_id);
wdenk's avatar
wdenk committed
927 928 929 930

	/*
	 * Copy options from OFFER packet if present
	 */
Justin Flammia's avatar
Justin Flammia committed
931 932

	/* Copy offered IP into the parameters request list */
933 934 935
	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
936

937 938
	iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
	pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
939 940
	bcast_ip.s_addr = 0xFFFFFFFFL;
	net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
wdenk's avatar
wdenk committed
941

942 943 944
#ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY
	udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY);
#endif	/* CONFIG_BOOTP_DHCP_REQUEST_DELAY */
945
	debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen);
946
	net_send_packet(net_tx_packet, pktlen);
wdenk's avatar
wdenk committed
947 948 949 950 951
}

/*
 *	Handle DHCP received packets.
 */
952 953
static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
			 unsigned src, unsigned len)
wdenk's avatar
wdenk committed
954
{
955
	struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
wdenk's avatar
wdenk committed
956

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

960
	/* Filter out pkts we don't want */
961
	if (check_packet(pkt, dest, src, len))
wdenk's avatar
wdenk committed
962 963
		return;

964 965
	debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: "
	      "%d\n", src, dest, len, dhcp_state);
wdenk's avatar
wdenk committed
966 967 968 969 970

	switch (dhcp_state) {
	case SELECTING:
		/*
		 * Wait an appropriate time for any potential DHCPOFFER packets
971 972 973
		 * 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
974
		 */
975
		debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file);
976
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
977
		if (strncmp(bp->bp_file,
978
			    CONFIG_SYS_BOOTFILE_PREFIX,
979
			    strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
980
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
wdenk's avatar
wdenk committed
981

982
			debug("TRANSITIONING TO REQUESTING STATE\n");
wdenk's avatar
wdenk committed
983
			dhcp_state = REQUESTING;
984

985
			if (net_read_u32((u32 *)&bp->bp_vend[0]) ==
986
						htonl(BOOTP_VENDOR_MAGIC))
987
				dhcp_process_options((u8 *)&bp->bp_vend[4], bp);
wdenk's avatar
wdenk committed
988

989
			net_set_timeout_handler(5000, bootp_timeout_handler);
990
			dhcp_send_request_packet(bp);
991
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
wdenk's avatar
wdenk committed
992
		}
993
#endif	/* CONFIG_SYS_BOOTFILE_PREFIX */
wdenk's avatar
wdenk committed
994 995 996 997

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

1000
		if (dhcp_message_type((u8 *)bp->bp_vend) == DHCP_ACK) {
1001
			if (net_read_u32((u32 *)&bp->bp_vend[0]) ==
1002
						htonl(BOOTP_VENDOR_MAGIC))
1003
				dhcp_process_options((u8 *)&bp->bp_vend[4], bp);
1004
			/* Store net params from reply */
1005
			store_net_params(bp);
wdenk's avatar
wdenk committed
1006
			dhcp_state = BOUND;
1007
			printf("DHCP client bound to address %pI4 (%lu ms)\n",
1008
			       &net_ip, get_timer(bootp_start));
1009
			bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP,
1010
					    "bootp_stop");
wdenk's avatar
wdenk committed
1011

1012
			net_auto_load();
wdenk's avatar
wdenk committed
1013 1014 1015
			return;
		}
		break;
1016 1017 1018
	case BOUND:
		/* DHCP client bound to address */
		break;
wdenk's avatar
wdenk committed
1019
	default:
1020
		puts("DHCP: INVALID STATE\n");
wdenk's avatar
wdenk committed
1021 1022 1023 1024
		break;
	}
}

1025
void dhcp_request(void)
wdenk's avatar
wdenk committed
1026
{
1027
	bootp_request();
wdenk's avatar
wdenk committed
1028
}
1029
#endif	/* CONFIG_CMD_DHCP */