#include <common.h>
#include <command.h>
+#include <efi_loader.h>
#include <net.h>
+#include <net/tftp.h>
#include "bootp.h"
-#include "tftp.h"
-#include "nfs.h"
-#ifdef CONFIG_STATUS_LED
+#ifdef CONFIG_LED_STATUS
#include <status_led.h>
#endif
#ifdef CONFIG_BOOTP_RANDOM_DELAY
#define CONFIG_BOOTP_ID_CACHE_SIZE 4
#endif
-ulong bootp_ids[CONFIG_BOOTP_ID_CACHE_SIZE];
+u32 bootp_ids[CONFIG_BOOTP_ID_CACHE_SIZE];
unsigned int bootp_num_ids;
int bootp_try;
ulong bootp_start;
char net_hostname[32] = {0,}; /* Our hostname */
char net_root_path[64] = {0,}; /* Our bootpath */
+static ulong time_taken_max;
+
#if defined(CONFIG_CMD_DHCP)
static dhcp_state_t dhcp_state = INIT;
-static unsigned long dhcp_leasetime;
+static u32 dhcp_leasetime;
static struct in_addr dhcp_server_ip;
+static u8 dhcp_option_overload;
+#define OVERLOAD_FILE 1
+#define OVERLOAD_SNAME 2
static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
unsigned src, unsigned len);
return false;
}
-static int check_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len)
+static int check_reply_packet(uchar *pkt, unsigned dest, unsigned src,
+ unsigned len)
{
struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
int retval = 0;
retval = -1;
else if (len < sizeof(struct bootp_hdr) - OPT_FIELD_SIZE)
retval = -2;
- else if (bp->bp_op != OP_BOOTREQUEST &&
- bp->bp_op != OP_BOOTREPLY &&
- bp->bp_op != DHCP_OFFER &&
- bp->bp_op != DHCP_ACK &&
- bp->bp_op != DHCP_NAK)
+ else if (bp->bp_op != OP_BOOTREPLY)
retval = -3;
else if (bp->bp_htype != HWT_ETHER)
retval = -4;
else if (bp->bp_hlen != HWL_ETHER)
retval = -5;
- else if (!bootp_match_id(net_read_long((ulong *)&bp->bp_id)))
+ else if (!bootp_match_id(net_read_u32(&bp->bp_id)))
retval = -6;
+ else if (memcmp(bp->bp_chaddr, net_ethaddr, HWL_ETHER) != 0)
+ retval = -7;
debug("Filtering pkt = %d\n", retval);
{
#if !defined(CONFIG_BOOTP_SERVERIP)
struct in_addr tmp_ip;
+ bool overwrite_serverip = true;
+
+#if defined(CONFIG_BOOTP_PREFER_SERVERIP)
+ overwrite_serverip = false;
+#endif
net_copy_ip(&tmp_ip, &bp->bp_siaddr);
- if (tmp_ip.s_addr != 0)
+ if (tmp_ip.s_addr != 0 && (overwrite_serverip || !net_server_ip.s_addr))
net_copy_ip(&net_server_ip, &bp->bp_siaddr);
memcpy(net_server_ethaddr,
((struct ethernet_hdr *)net_rx_packet)->et_src, 6);
- if (strlen(bp->bp_file) > 0)
+ if (
+#if defined(CONFIG_CMD_DHCP)
+ !(dhcp_option_overload & OVERLOAD_FILE) &&
+#endif
+ (strlen(bp->bp_file) > 0) &&
+ !net_boot_file_name_explicit) {
copy_filename(net_boot_file_name, bp->bp_file,
sizeof(net_boot_file_name));
+ }
debug("net_boot_file_name: %s\n", net_boot_file_name);
* not contain a new value
*/
if (*net_boot_file_name)
- setenv("bootfile", net_boot_file_name);
+ env_set("bootfile", net_boot_file_name);
#endif
net_copy_ip(&net_ip, &bp->bp_yiaddr);
}
debug("net_nis_domain : %s\n", net_nis_domain);
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
- if (net_ntp_server)
+ if (net_ntp_server.s_addr)
debug("net_ntp_server : %pI4\n", &net_ntp_server);
#endif
}
bp = (struct bootp_hdr *)pkt;
/* Filter out pkts we don't want */
- if (check_packet(pkt, dest, src, len))
+ if (check_reply_packet(pkt, dest, src, len))
return;
/*
* Got a good BOOTP reply. Copy the data into our variables.
*/
-#ifdef CONFIG_STATUS_LED
- status_led_set(STATUS_LED_BOOT, STATUS_LED_OFF);
+#if defined(CONFIG_LED_STATUS) && defined(CONFIG_LED_STATUS_BOOT_ENABLE)
+ status_led_set(CONFIG_LED_STATUS_BOOT, CONFIG_LED_STATUS_OFF);
#endif
store_net_params(bp); /* Store net parameters from reply */
/* Retrieve extended information (we must parse the vendor area) */
- if (net_read_long((ulong *)&bp->bp_vend[0]) ==
- htonl(BOOTP_VENDOR_MAGIC))
+ if (net_read_u32((u32 *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC))
bootp_process_vendor((uchar *)&bp->bp_vend[4], len);
net_set_timeout_handler(0, (thand_f *)0);
{
ulong time_taken = get_timer(bootp_start);
- if (time_taken >= TIMEOUT_MS) {
+ if (time_taken >= time_taken_max) {
#ifdef CONFIG_BOOTP_MAY_FAIL
- puts("\nRetry time exceeded\n");
- net_set_state(NETLOOP_FAIL);
-#else
- puts("\nRetry time exceeded; starting again\n");
- net_start_again();
+ char *ethrotate;
+
+ ethrotate = env_get("ethrotate");
+ if ((ethrotate && strcmp(ethrotate, "no") == 0) ||
+ net_restart_wrap) {
+ puts("\nRetry time exceeded\n");
+ net_set_state(NETLOOP_FAIL);
+ } else
#endif
+ {
+ puts("\nRetry time exceeded; starting again\n");
+ net_start_again();
+ }
} else {
bootp_timeout *= 2;
if (bootp_timeout > 2000)
e += vci_strlen; \
} while (0)
+static u8 *add_vci(u8 *e)
+{
+ char *vci = NULL;
+ char *env_vci = env_get("bootp_vci");
+
+#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING)
+ vci = CONFIG_SPL_NET_VCI_STRING;
+#elif defined(CONFIG_BOOTP_VCI_STRING)
+ vci = CONFIG_BOOTP_VCI_STRING;
+#endif
+
+ if (env_vci)
+ vci = env_vci;
+
+ if (vci)
+ put_vci(e, vci);
+
+ return e;
+}
+
/*
* Initialize BOOTP extension fields in the request.
*/
{
u8 *start = e;
u8 *cnt;
-#if defined(CONFIG_BOOTP_PXE)
+#ifdef CONFIG_LIB_UUID
char *uuid;
- u16 clientarch;
#endif
+ int clientarch = -1;
#if defined(CONFIG_BOOTP_VENDOREX)
u8 *x;
*e++ = tmp & 0xff;
}
#if defined(CONFIG_BOOTP_SEND_HOSTNAME)
- hostname = getenv("hostname");
+ hostname = env_get("hostname");
if (hostname) {
int hostnamelen = strlen(hostname);
}
#endif
-#if defined(CONFIG_BOOTP_PXE)
+#ifdef CONFIG_BOOTP_PXE_CLIENTARCH
clientarch = CONFIG_BOOTP_PXE_CLIENTARCH;
- *e++ = 93; /* Client System Architecture */
- *e++ = 2;
- *e++ = (clientarch >> 8) & 0xff;
- *e++ = clientarch & 0xff;
+#endif
+
+ if (env_get("bootp_arch"))
+ clientarch = env_get_ulong("bootp_arch", 16, clientarch);
+
+ if (clientarch > 0) {
+ *e++ = 93; /* Client System Architecture */
+ *e++ = 2;
+ *e++ = (clientarch >> 8) & 0xff;
+ *e++ = clientarch & 0xff;
+ }
*e++ = 94; /* Client Network Interface Identifier */
*e++ = 3;
*e++ = 0; /* major revision */
*e++ = 0; /* minor revision */
- uuid = getenv("pxeuuid");
+#ifdef CONFIG_LIB_UUID
+ uuid = env_get("pxeuuid");
if (uuid) {
if (uuid_str_valid(uuid)) {
}
#endif
-#ifdef CONFIG_BOOTP_VCI_STRING
- put_vci(e, CONFIG_BOOTP_VCI_STRING);
-#endif
+ e = add_vci(e);
#if defined(CONFIG_BOOTP_VENDOREX)
x = dhcp_vendorex_prep(e);
*e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
#endif
-#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
- put_vci(e, CONFIG_BOOTP_VCI_STRING);
-#endif
-#endif
+ add_vci(e);
#if defined(CONFIG_BOOTP_SUBNETMASK)
*e++ = 1; /* Subnet mask request */
*e++ = 255; /* End of the list */
+ /*
+ * 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;
+ }
+
return e - start;
}
#endif
#ifdef CONFIG_BOOTP_RANDOM_DELAY
ulong rand_ms;
#endif
- ulong bootp_id;
+ u32 bootp_id;
struct in_addr zero_ip;
struct in_addr bcast_ip;
+ char *ep; /* Environment pointer */
bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
#if defined(CONFIG_CMD_DHCP)
dhcp_state = INIT;
#endif
+ ep = env_get("bootpretryperiod");
+ if (ep != NULL)
+ time_taken_max = simple_strtoul(ep, NULL, 10);
+ else
+ time_taken_max = TIMEOUT_MS;
+
#ifdef CONFIG_BOOTP_RANDOM_DELAY /* Random BOOTP delay */
if (bootp_try == 0)
srand_mac();
bp->bp_htype = HWT_ETHER;
bp->bp_hlen = HWL_ETHER;
bp->bp_hops = 0;
- bp->bp_secs = htons(get_timer(0) / 1000);
+ /*
+ * according to RFC1542, should be 0 on first request, secs since
+ * first request otherwise
+ */
+ bp->bp_secs = htons(get_timer(bootp_start) / 1000);
zero_ip.s_addr = 0;
net_write_ip(&bp->bp_ciaddr, zero_ip);
net_write_ip(&bp->bp_yiaddr, zero_ip);
* Bootp ID is the lower 4 bytes of our ethernet address
* plus the current time in ms.
*/
- bootp_id = ((ulong)net_ethaddr[2] << 24)
- | ((ulong)net_ethaddr[3] << 16)
- | ((ulong)net_ethaddr[4] << 8)
- | (ulong)net_ethaddr[5];
+ bootp_id = ((u32)net_ethaddr[2] << 24)
+ | ((u32)net_ethaddr[3] << 16)
+ | ((u32)net_ethaddr[4] << 8)
+ | (u32)net_ethaddr[5];
bootp_id += get_timer(0);
bootp_id = htonl(bootp_id);
bootp_add_id(bootp_id);
- net_copy_long(&bp->bp_id, &bootp_id);
+ net_copy_u32(&bp->bp_id, &bootp_id);
/*
* Calculate proper packet lengths taking into account the
}
#if defined(CONFIG_CMD_DHCP)
-static void dhcp_process_options(uchar *popt, struct bootp_hdr *bp)
+static void dhcp_process_options(uchar *popt, uchar *end)
{
- uchar *end = popt + BOOTP_HDR_SIZE;
int oplen, size;
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
int *to_ptr;
while (popt < end && *popt != 0xff) {
oplen = *(popt + 1);
switch (*popt) {
+ case 0:
+ oplen = -1; /* Pad omits len byte */
+ break;
case 1:
net_copy_ip(&net_netmask, (popt + 2));
break;
#if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
case 2: /* Time offset */
to_ptr = &net_ntp_time_offset;
- net_copy_long((ulong *)to_ptr, (ulong *)(popt + 2));
+ net_copy_u32((u32 *)to_ptr, (u32 *)(popt + 2));
net_ntp_time_offset = ntohl(net_ntp_time_offset);
break;
#endif
break;
#endif
case 51:
- net_copy_long(&dhcp_leasetime, (ulong *)(popt + 2));
+ net_copy_u32(&dhcp_leasetime, (u32 *)(popt + 2));
+ break;
+ case 52:
+ dhcp_option_overload = popt[2];
break;
case 53: /* Ignore Message Type Option */
break;
break;
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
- */
- size = truncate_sz("Opt Boot File",
- sizeof(bp->bp_file),
- oplen);
- if (bp->bp_file[0] == '\0' && size > 0) {
- /*
- * only use vendor boot file if we didn't
- * receive a boot file in the main non-vendor
- * part of the packet - god only knows why
- * some vendors chose not to use this perfectly
- * good spot to store the boot file (join on
- * Tru64 Unix) it seems mind bogglingly crazy
- * to me
- */
- printf("*** WARNING: using vendor "
- "optional boot file\n");
- memcpy(bp->bp_file, popt + 2, size);
- bp->bp_file[size] = '\0';
+ case 67: /* Bootfile option */
+ if (!net_boot_file_name_explicit) {
+ size = truncate_sz("Bootfile",
+ sizeof(net_boot_file_name),
+ oplen);
+ memcpy(&net_boot_file_name, popt + 2, size);
+ net_boot_file_name[size] = 0;
}
break;
default:
}
}
+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);
+ }
+}
+
static int dhcp_message_type(unsigned char *popt)
{
- if (net_read_long((ulong *)popt) != htonl(BOOTP_VENDOR_MAGIC))
+ if (net_read_u32((u32 *)popt) != htonl(BOOTP_VENDOR_MAGIC))
return -1;
popt += 4;
while (*popt != 0xff) {
if (*popt == 53) /* DHCP Message Type */
return *(popt + 2);
- popt += *(popt + 1) + 2; /* Scan through all options */
+ if (*popt == 0) {
+ /* Pad */
+ popt += 1;
+ } else {
+ /* Scan through all options */
+ popt += *(popt + 1) + 2;
+ }
}
return -1;
}
bp->bp_htype = HWT_ETHER;
bp->bp_hlen = HWL_ETHER;
bp->bp_hops = 0;
- bp->bp_secs = htons(get_timer(0) / 1000);
+ bp->bp_secs = htons(get_timer(bootp_start) / 1000);
/* Do not set the client IP, your IP, or server IP yet, since it
* hasn't been ACK'ed by the server yet */
net_write_ip(&bp->bp_giaddr, zero_ip);
memcpy(bp->bp_chaddr, net_ethaddr, 6);
+ copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
/*
* ID is the id of the OFFER packet
*/
- net_copy_long(&bp->bp_id, &bp_offer->bp_id);
+ net_copy_u32(&bp->bp_id, &bp_offer->bp_id);
/*
* Copy options from OFFER packet if present
src, dest, len, dhcp_state);
/* Filter out pkts we don't want */
- if (check_packet(pkt, dest, src, len))
+ if (check_reply_packet(pkt, dest, src, len))
return;
debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: "
"%d\n", src, dest, len, dhcp_state);
+ if (net_read_ip(&bp->bp_yiaddr).s_addr == 0)
+ return;
+
switch (dhcp_state) {
case SELECTING:
/*
CONFIG_SYS_BOOTFILE_PREFIX,
strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
#endif /* CONFIG_SYS_BOOTFILE_PREFIX */
+ dhcp_packet_process_options(bp);
+ efi_net_set_dhcp_ack(pkt, len);
debug("TRANSITIONING TO REQUESTING STATE\n");
dhcp_state = REQUESTING;
- if (net_read_long((ulong *)&bp->bp_vend[0]) ==
- htonl(BOOTP_VENDOR_MAGIC))
- dhcp_process_options((u8 *)&bp->bp_vend[4], bp);
-
net_set_timeout_handler(5000, bootp_timeout_handler);
dhcp_send_request_packet(bp);
#ifdef CONFIG_SYS_BOOTFILE_PREFIX
debug("DHCP State: REQUESTING\n");
if (dhcp_message_type((u8 *)bp->bp_vend) == DHCP_ACK) {
- if (net_read_long((ulong *)&bp->bp_vend[0]) ==
- htonl(BOOTP_VENDOR_MAGIC))
- dhcp_process_options((u8 *)&bp->bp_vend[4], bp);
+ dhcp_packet_process_options(bp);
/* Store net params from reply */
store_net_params(bp);
dhcp_state = BOUND;
printf("DHCP client bound to address %pI4 (%lu ms)\n",
&net_ip, get_timer(bootp_start));
+ net_set_timeout_handler(0, (thand_f *)0);
bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP,
"bootp_stop");