13861aa5cSwdenk /*
23861aa5cSwdenk * Based on LiMon - BOOTP.
33861aa5cSwdenk *
43861aa5cSwdenk * Copyright 1994, 1995, 2000 Neil Russell.
53861aa5cSwdenk * (See License)
63861aa5cSwdenk * Copyright 2000 Roland Borde
73861aa5cSwdenk * Copyright 2000 Paolo Scaffardi
8232c150aSwdenk * Copyright 2000-2004 Wolfgang Denk, wd@denx.de
93861aa5cSwdenk */
103861aa5cSwdenk
113861aa5cSwdenk #include <common.h>
123861aa5cSwdenk #include <command.h>
130efe1bcfSAlexander Graf #include <efi_loader.h>
143861aa5cSwdenk #include <net.h>
1534696958SLukasz Majewski #include <net/tftp.h>
163861aa5cSwdenk #include "bootp.h"
172d8d190cSUri Mashiach #ifdef CONFIG_LED_STATUS
183861aa5cSwdenk #include <status_led.h>
193861aa5cSwdenk #endif
20db7720baSKim Phillips #ifdef CONFIG_BOOTP_RANDOM_DELAY
21db7720baSKim Phillips #include "net_rand.h"
22db7720baSKim Phillips #endif
233861aa5cSwdenk
243861aa5cSwdenk #define BOOTP_VENDOR_MAGIC 0x63825363 /* RFC1048 Magic Cookie */
253861aa5cSwdenk
26f59be6e8SStephen Warren /*
27f59be6e8SStephen Warren * The timeout for the initial BOOTP/DHCP request used to be described by a
28f59be6e8SStephen Warren * counter of fixed-length timeout periods. TIMEOUT_COUNT represents
29f59be6e8SStephen Warren * that counter
30f59be6e8SStephen Warren *
31f59be6e8SStephen Warren * Now that the timeout periods are variable (exponential backoff and retry)
32f59be6e8SStephen Warren * we convert the timeout count to the absolute time it would have take to
33f59be6e8SStephen Warren * execute that many retries, and keep sending retry packets until that time
34f59be6e8SStephen Warren * is reached.
35f59be6e8SStephen Warren */
363861aa5cSwdenk #ifndef CONFIG_NET_RETRY_COUNT
373861aa5cSwdenk # define TIMEOUT_COUNT 5 /* # of timeouts before giving up */
383861aa5cSwdenk #else
393861aa5cSwdenk # define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT)
403861aa5cSwdenk #endif
41f59be6e8SStephen Warren #define TIMEOUT_MS ((3 + (TIMEOUT_COUNT * 5)) * 1000)
423861aa5cSwdenk
433861aa5cSwdenk #define PORT_BOOTPS 67 /* BOOTP server UDP port */
443861aa5cSwdenk #define PORT_BOOTPC 68 /* BOOTP client UDP port */
453861aa5cSwdenk
463861aa5cSwdenk #ifndef CONFIG_DHCP_MIN_EXT_LEN /* minimal length of extension list */
473861aa5cSwdenk #define CONFIG_DHCP_MIN_EXT_LEN 64
483861aa5cSwdenk #endif
493861aa5cSwdenk
5092ac8accSThierry Reding #ifndef CONFIG_BOOTP_ID_CACHE_SIZE
5192ac8accSThierry Reding #define CONFIG_BOOTP_ID_CACHE_SIZE 4
5292ac8accSThierry Reding #endif
5392ac8accSThierry Reding
545917e7d1SSergey Temerkhanov u32 bootp_ids[CONFIG_BOOTP_ID_CACHE_SIZE];
5592ac8accSThierry Reding unsigned int bootp_num_ids;
567044c6bbSJoe Hershberger int bootp_try;
57f59be6e8SStephen Warren ulong bootp_start;
58f59be6e8SStephen Warren ulong bootp_timeout;
59586cbe51SJoe Hershberger char net_nis_domain[32] = {0,}; /* Our NIS domain */
60586cbe51SJoe Hershberger char net_hostname[32] = {0,}; /* Our hostname */
61586cbe51SJoe Hershberger char net_root_path[64] = {0,}; /* Our bootpath */
623861aa5cSwdenk
6350768f5bSAlexandre Messier static ulong time_taken_max;
6450768f5bSAlexandre Messier
65643d1ab2SJon Loeliger #if defined(CONFIG_CMD_DHCP)
6606370590SKim Phillips static dhcp_state_t dhcp_state = INIT;
675917e7d1SSergey Temerkhanov static u32 dhcp_leasetime;
68049a95a7SJoe Hershberger static struct in_addr dhcp_server_ip;
69ec87b1b3SStefan Brüns static u8 dhcp_option_overload;
70ec87b1b3SStefan Brüns #define OVERLOAD_FILE 1
71ec87b1b3SStefan Brüns #define OVERLOAD_SNAME 2
72049a95a7SJoe Hershberger static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
73049a95a7SJoe Hershberger unsigned src, unsigned len);
743861aa5cSwdenk
753861aa5cSwdenk /* For Debug */
763e38691eSwdenk #if 0
773e38691eSwdenk static char *dhcpmsg2str(int type)
783861aa5cSwdenk {
793861aa5cSwdenk switch (type) {
803861aa5cSwdenk case 1: return "DHCPDISCOVER"; break;
813861aa5cSwdenk case 2: return "DHCPOFFER"; break;
823861aa5cSwdenk case 3: return "DHCPREQUEST"; break;
833861aa5cSwdenk case 4: return "DHCPDECLINE"; break;
843861aa5cSwdenk case 5: return "DHCPACK"; break;
853861aa5cSwdenk case 6: return "DHCPNACK"; break;
863861aa5cSwdenk case 7: return "DHCPRELEASE"; break;
873861aa5cSwdenk default: return "UNKNOWN/INVALID MSG TYPE"; break;
883861aa5cSwdenk }
893861aa5cSwdenk }
903e38691eSwdenk #endif
91610f2e9cSJon Loeliger #endif
923861aa5cSwdenk
bootp_add_id(ulong id)9392ac8accSThierry Reding static void bootp_add_id(ulong id)
9492ac8accSThierry Reding {
9592ac8accSThierry Reding if (bootp_num_ids >= ARRAY_SIZE(bootp_ids)) {
9692ac8accSThierry Reding size_t size = sizeof(bootp_ids) - sizeof(id);
9792ac8accSThierry Reding
9892ac8accSThierry Reding memmove(bootp_ids, &bootp_ids[1], size);
9992ac8accSThierry Reding bootp_ids[bootp_num_ids - 1] = id;
10092ac8accSThierry Reding } else {
10192ac8accSThierry Reding bootp_ids[bootp_num_ids] = id;
10292ac8accSThierry Reding bootp_num_ids++;
10392ac8accSThierry Reding }
10492ac8accSThierry Reding }
10592ac8accSThierry Reding
bootp_match_id(ulong id)10692ac8accSThierry Reding static bool bootp_match_id(ulong id)
10792ac8accSThierry Reding {
10892ac8accSThierry Reding unsigned int i;
10992ac8accSThierry Reding
11092ac8accSThierry Reding for (i = 0; i < bootp_num_ids; i++)
11192ac8accSThierry Reding if (bootp_ids[i] == id)
11292ac8accSThierry Reding return true;
11392ac8accSThierry Reding
11492ac8accSThierry Reding return false;
11592ac8accSThierry Reding }
11692ac8accSThierry Reding
check_reply_packet(uchar * pkt,unsigned dest,unsigned src,unsigned len)117867d6ae2SStefan Brüns static int check_reply_packet(uchar *pkt, unsigned dest, unsigned src,
118867d6ae2SStefan Brüns unsigned len)
1193861aa5cSwdenk {
1207044c6bbSJoe Hershberger struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
1213861aa5cSwdenk int retval = 0;
1223861aa5cSwdenk
1233861aa5cSwdenk if (dest != PORT_BOOTPC || src != PORT_BOOTPS)
1243861aa5cSwdenk retval = -1;
1257044c6bbSJoe Hershberger else if (len < sizeof(struct bootp_hdr) - OPT_FIELD_SIZE)
1263861aa5cSwdenk retval = -2;
127867d6ae2SStefan Brüns else if (bp->bp_op != OP_BOOTREPLY)
1283861aa5cSwdenk retval = -3;
1293861aa5cSwdenk else if (bp->bp_htype != HWT_ETHER)
1303861aa5cSwdenk retval = -4;
1313861aa5cSwdenk else if (bp->bp_hlen != HWL_ETHER)
1323861aa5cSwdenk retval = -5;
1335917e7d1SSergey Temerkhanov else if (!bootp_match_id(net_read_u32(&bp->bp_id)))
1343861aa5cSwdenk retval = -6;
135214cc905SAnton Persson else if (memcmp(bp->bp_chaddr, net_ethaddr, HWL_ETHER) != 0)
136214cc905SAnton Persson retval = -7;
1373861aa5cSwdenk
1383861aa5cSwdenk debug("Filtering pkt = %d\n", retval);
1393861aa5cSwdenk
1403861aa5cSwdenk return retval;
1413861aa5cSwdenk }
1423861aa5cSwdenk
1433861aa5cSwdenk /*
1443861aa5cSwdenk * Copy parameters of interest from BOOTP_REPLY/DHCP_OFFER packet
1453861aa5cSwdenk */
store_net_params(struct bootp_hdr * bp)1467044c6bbSJoe Hershberger static void store_net_params(struct bootp_hdr *bp)
1473861aa5cSwdenk {
1485d110f0aSWilson Callan #if !defined(CONFIG_BOOTP_SERVERIP)
149049a95a7SJoe Hershberger struct in_addr tmp_ip;
150*bdce340cSAlexander Graf bool overwrite_serverip = true;
151*bdce340cSAlexander Graf
152*bdce340cSAlexander Graf #if defined(CONFIG_BOOTP_PREFER_SERVERIP)
153*bdce340cSAlexander Graf overwrite_serverip = false;
154*bdce340cSAlexander Graf #endif
1551752f0fdSJoe Hershberger
156049a95a7SJoe Hershberger net_copy_ip(&tmp_ip, &bp->bp_siaddr);
157*bdce340cSAlexander Graf if (tmp_ip.s_addr != 0 && (overwrite_serverip || !net_server_ip.s_addr))
158049a95a7SJoe Hershberger net_copy_ip(&net_server_ip, &bp->bp_siaddr);
1591203fcceSJoe Hershberger memcpy(net_server_ethaddr,
1601203fcceSJoe Hershberger ((struct ethernet_hdr *)net_rx_packet)->et_src, 6);
161ec87b1b3SStefan Brüns if (
162ec87b1b3SStefan Brüns #if defined(CONFIG_CMD_DHCP)
163ec87b1b3SStefan Brüns !(dhcp_option_overload & OVERLOAD_FILE) &&
164ec87b1b3SStefan Brüns #endif
165449312c1SAlexander Graf (strlen(bp->bp_file) > 0) &&
166449312c1SAlexander Graf !net_boot_file_name_explicit) {
1671411157dSJoe Hershberger copy_filename(net_boot_file_name, bp->bp_file,
1681411157dSJoe Hershberger sizeof(net_boot_file_name));
169ec87b1b3SStefan Brüns }
1703861aa5cSwdenk
1711411157dSJoe Hershberger debug("net_boot_file_name: %s\n", net_boot_file_name);
1723861aa5cSwdenk
1733861aa5cSwdenk /* Propagate to environment:
1743861aa5cSwdenk * don't delete exising entry when BOOTP / DHCP reply does
1753861aa5cSwdenk * not contain a new value
1763861aa5cSwdenk */
1771411157dSJoe Hershberger if (*net_boot_file_name)
178382bee57SSimon Glass env_set("bootfile", net_boot_file_name);
179ecec4e9cSWu, Josh #endif
180049a95a7SJoe Hershberger net_copy_ip(&net_ip, &bp->bp_yiaddr);
1813861aa5cSwdenk }
1823861aa5cSwdenk
truncate_sz(const char * name,int maxlen,int curlen)1833861aa5cSwdenk static int truncate_sz(const char *name, int maxlen, int curlen)
1843861aa5cSwdenk {
1853861aa5cSwdenk if (curlen >= maxlen) {
1863090b7e3SJoe Hershberger printf("*** WARNING: %s is too long (%d - max: %d)"
1873090b7e3SJoe Hershberger " - truncated\n", name, curlen, maxlen);
1883861aa5cSwdenk curlen = maxlen - 1;
1893861aa5cSwdenk }
1903090b7e3SJoe Hershberger return curlen;
1913861aa5cSwdenk }
1923861aa5cSwdenk
193643d1ab2SJon Loeliger #if !defined(CONFIG_CMD_DHCP)
1943861aa5cSwdenk
bootp_process_vendor_field(u8 * ext)1957044c6bbSJoe Hershberger static void bootp_process_vendor_field(u8 *ext)
1963861aa5cSwdenk {
1973861aa5cSwdenk int size = *(ext + 1);
1983861aa5cSwdenk
1990ebf04c6SRobin Getz debug("[BOOTP] Processing extension %d... (%d bytes)\n", *ext,
200232c150aSwdenk *(ext + 1));
2013861aa5cSwdenk
2021411157dSJoe Hershberger net_boot_file_expected_size_in_blocks = 0;
2033861aa5cSwdenk
2043861aa5cSwdenk switch (*ext) {
2053861aa5cSwdenk /* Fixed length fields */
2063861aa5cSwdenk case 1: /* Subnet mask */
207049a95a7SJoe Hershberger if (net_netmask.s_addr == 0)
208049a95a7SJoe Hershberger net_copy_ip(&net_netmask, (struct in_addr *)(ext + 2));
2093861aa5cSwdenk break;
2103861aa5cSwdenk case 2: /* Time offset - Not yet supported */
2113861aa5cSwdenk break;
2123861aa5cSwdenk /* Variable length fields */
2133861aa5cSwdenk case 3: /* Gateways list */
214049a95a7SJoe Hershberger if (net_gateway.s_addr == 0)
215049a95a7SJoe Hershberger net_copy_ip(&net_gateway, (struct in_addr *)(ext + 2));
2163861aa5cSwdenk break;
2173861aa5cSwdenk case 4: /* Time server - Not yet supported */
2183861aa5cSwdenk break;
2193861aa5cSwdenk case 5: /* IEN-116 name server - Not yet supported */
2203861aa5cSwdenk break;
2213861aa5cSwdenk case 6:
222049a95a7SJoe Hershberger if (net_dns_server.s_addr == 0)
223049a95a7SJoe Hershberger net_copy_ip(&net_dns_server,
224049a95a7SJoe Hershberger (struct in_addr *)(ext + 2));
2251fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_DNS2)
226049a95a7SJoe Hershberger if ((net_dns_server2.s_addr == 0) && (size > 4))
227049a95a7SJoe Hershberger net_copy_ip(&net_dns_server2,
228049a95a7SJoe Hershberger (struct in_addr *)(ext + 2 + 4));
229fe389a82Sstroese #endif
2303861aa5cSwdenk break;
2313861aa5cSwdenk case 7: /* Log server - Not yet supported */
2323861aa5cSwdenk break;
2333861aa5cSwdenk case 8: /* Cookie/Quote server - Not yet supported */
2343861aa5cSwdenk break;
2353861aa5cSwdenk case 9: /* LPR server - Not yet supported */
2363861aa5cSwdenk break;
2373861aa5cSwdenk case 10: /* Impress server - Not yet supported */
2383861aa5cSwdenk break;
2393861aa5cSwdenk case 11: /* RPL server - Not yet supported */
2403861aa5cSwdenk break;
2413861aa5cSwdenk case 12: /* Host name */
242586cbe51SJoe Hershberger if (net_hostname[0] == 0) {
2433090b7e3SJoe Hershberger size = truncate_sz("Host Name",
244586cbe51SJoe Hershberger sizeof(net_hostname), size);
245586cbe51SJoe Hershberger memcpy(&net_hostname, ext + 2, size);
246586cbe51SJoe Hershberger net_hostname[size] = 0;
2473861aa5cSwdenk }
2483861aa5cSwdenk break;
2493861aa5cSwdenk case 13: /* Boot file size */
2503861aa5cSwdenk if (size == 2)
2511411157dSJoe Hershberger net_boot_file_expected_size_in_blocks =
2521411157dSJoe Hershberger ntohs(*(ushort *)(ext + 2));
2533861aa5cSwdenk else if (size == 4)
2541411157dSJoe Hershberger net_boot_file_expected_size_in_blocks =
2551411157dSJoe Hershberger ntohl(*(ulong *)(ext + 2));
2563861aa5cSwdenk break;
2573861aa5cSwdenk case 14: /* Merit dump file - Not yet supported */
2583861aa5cSwdenk break;
2593861aa5cSwdenk case 15: /* Domain name - Not yet supported */
2603861aa5cSwdenk break;
2613861aa5cSwdenk case 16: /* Swap server - Not yet supported */
2623861aa5cSwdenk break;
2633861aa5cSwdenk case 17: /* Root path */
264586cbe51SJoe Hershberger if (net_root_path[0] == 0) {
2653090b7e3SJoe Hershberger size = truncate_sz("Root Path",
266586cbe51SJoe Hershberger sizeof(net_root_path), size);
267586cbe51SJoe Hershberger memcpy(&net_root_path, ext + 2, size);
268586cbe51SJoe Hershberger net_root_path[size] = 0;
2693861aa5cSwdenk }
2703861aa5cSwdenk break;
2713861aa5cSwdenk case 18: /* Extension path - Not yet supported */
2723861aa5cSwdenk /*
27347cd00faSwdenk * This can be used to send the information of the
2743861aa5cSwdenk * vendor area in another file that the client can
2753861aa5cSwdenk * access via TFTP.
2763861aa5cSwdenk */
2773861aa5cSwdenk break;
2783861aa5cSwdenk /* IP host layer fields */
2793861aa5cSwdenk case 40: /* NIS Domain name */
280586cbe51SJoe Hershberger if (net_nis_domain[0] == 0) {
2813090b7e3SJoe Hershberger size = truncate_sz("NIS Domain Name",
282586cbe51SJoe Hershberger sizeof(net_nis_domain), size);
283586cbe51SJoe Hershberger memcpy(&net_nis_domain, ext + 2, size);
284586cbe51SJoe Hershberger net_nis_domain[size] = 0;
2853861aa5cSwdenk }
2863861aa5cSwdenk break;
28709e3a67dSLuuk Paulussen #if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
28809e3a67dSLuuk Paulussen case 42: /* NTP server IP */
289049a95a7SJoe Hershberger net_copy_ip(&net_ntp_server, (struct in_addr *)(ext + 2));
29009e3a67dSLuuk Paulussen break;
29109e3a67dSLuuk Paulussen #endif
2923861aa5cSwdenk /* Application layer fields */
2933861aa5cSwdenk case 43: /* Vendor specific info - Not yet supported */
2943861aa5cSwdenk /*
29547cd00faSwdenk * Binary information to exchange specific
2963861aa5cSwdenk * product information.
2973861aa5cSwdenk */
2983861aa5cSwdenk break;
2993861aa5cSwdenk /* Reserved (custom) fields (128..254) */
3003861aa5cSwdenk }
3013861aa5cSwdenk }
3023861aa5cSwdenk
bootp_process_vendor(u8 * ext,int size)3037044c6bbSJoe Hershberger static void bootp_process_vendor(u8 *ext, int size)
3043861aa5cSwdenk {
3053861aa5cSwdenk u8 *end = ext + size;
3063861aa5cSwdenk
3070ebf04c6SRobin Getz debug("[BOOTP] Checking extension (%d bytes)...\n", size);
3083861aa5cSwdenk
3093861aa5cSwdenk while ((ext < end) && (*ext != 0xff)) {
3103861aa5cSwdenk if (*ext == 0) {
3113861aa5cSwdenk ext++;
3123861aa5cSwdenk } else {
3133861aa5cSwdenk u8 *opt = ext;
314232c150aSwdenk
3153861aa5cSwdenk ext += ext[1] + 2;
3163861aa5cSwdenk if (ext <= end)
3177044c6bbSJoe Hershberger bootp_process_vendor_field(opt);
3183861aa5cSwdenk }
3193861aa5cSwdenk }
3203861aa5cSwdenk
3210ebf04c6SRobin Getz debug("[BOOTP] Received fields:\n");
322049a95a7SJoe Hershberger if (net_netmask.s_addr)
323049a95a7SJoe Hershberger debug("net_netmask : %pI4\n", &net_netmask);
3243861aa5cSwdenk
325049a95a7SJoe Hershberger if (net_gateway.s_addr)
326049a95a7SJoe Hershberger debug("net_gateway : %pI4", &net_gateway);
3273861aa5cSwdenk
3281411157dSJoe Hershberger if (net_boot_file_expected_size_in_blocks)
3291411157dSJoe Hershberger debug("net_boot_file_expected_size_in_blocks : %d\n",
3301411157dSJoe Hershberger net_boot_file_expected_size_in_blocks);
3313861aa5cSwdenk
332586cbe51SJoe Hershberger if (net_hostname[0])
333586cbe51SJoe Hershberger debug("net_hostname : %s\n", net_hostname);
3343861aa5cSwdenk
335586cbe51SJoe Hershberger if (net_root_path[0])
336586cbe51SJoe Hershberger debug("net_root_path : %s\n", net_root_path);
3373861aa5cSwdenk
338586cbe51SJoe Hershberger if (net_nis_domain[0])
339586cbe51SJoe Hershberger debug("net_nis_domain : %s\n", net_nis_domain);
3403861aa5cSwdenk
34109e3a67dSLuuk Paulussen #if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
3424b4dc521SChris Packham if (net_ntp_server.s_addr)
343049a95a7SJoe Hershberger debug("net_ntp_server : %pI4\n", &net_ntp_server);
34409e3a67dSLuuk Paulussen #endif
3453861aa5cSwdenk }
34609349866SSimon Glass
3473861aa5cSwdenk /*
3483861aa5cSwdenk * Handle a BOOTP received packet.
3493861aa5cSwdenk */
bootp_handler(uchar * pkt,unsigned dest,struct in_addr sip,unsigned src,unsigned len)350049a95a7SJoe Hershberger static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
351049a95a7SJoe Hershberger unsigned src, unsigned len)
3523861aa5cSwdenk {
3537044c6bbSJoe Hershberger struct bootp_hdr *bp;
3543861aa5cSwdenk
355b64f190bSWolfgang Denk debug("got BOOTP packet (src=%d, dst=%d, len=%d want_len=%zu)\n",
3567044c6bbSJoe Hershberger src, dest, len, sizeof(struct bootp_hdr));
3573861aa5cSwdenk
3587044c6bbSJoe Hershberger bp = (struct bootp_hdr *)pkt;
3593861aa5cSwdenk
3603090b7e3SJoe Hershberger /* Filter out pkts we don't want */
361867d6ae2SStefan Brüns if (check_reply_packet(pkt, dest, src, len))
3623861aa5cSwdenk return;
3633861aa5cSwdenk
3643861aa5cSwdenk /*
3653861aa5cSwdenk * Got a good BOOTP reply. Copy the data into our variables.
3663861aa5cSwdenk */
3672d8d190cSUri Mashiach #if defined(CONFIG_LED_STATUS) && defined(CONFIG_LED_STATUS_BOOT_ENABLE)
3682d8d190cSUri Mashiach status_led_set(CONFIG_LED_STATUS_BOOT, CONFIG_LED_STATUS_OFF);
3693861aa5cSwdenk #endif
3703861aa5cSwdenk
3717044c6bbSJoe Hershberger store_net_params(bp); /* Store net parameters from reply */
3723861aa5cSwdenk
3733861aa5cSwdenk /* Retrieve extended information (we must parse the vendor area) */
3745917e7d1SSergey Temerkhanov if (net_read_u32((u32 *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC))
3757044c6bbSJoe Hershberger bootp_process_vendor((uchar *)&bp->bp_vend[4], len);
3763861aa5cSwdenk
377bc0571fcSJoe Hershberger net_set_timeout_handler(0, (thand_f *)0);
378573f14feSSimon Glass bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
3793861aa5cSwdenk
3803861aa5cSwdenk debug("Got good BOOTP\n");
3813861aa5cSwdenk
382e4a3d57dSSimon Glass net_auto_load();
3833861aa5cSwdenk }
384610f2e9cSJon Loeliger #endif
3853861aa5cSwdenk
3863861aa5cSwdenk /*
3873861aa5cSwdenk * Timeout on BOOTP/DHCP request.
3883861aa5cSwdenk */
bootp_timeout_handler(void)3897044c6bbSJoe Hershberger static void bootp_timeout_handler(void)
3903861aa5cSwdenk {
391f59be6e8SStephen Warren ulong time_taken = get_timer(bootp_start);
392f59be6e8SStephen Warren
39350768f5bSAlexandre Messier if (time_taken >= time_taken_max) {
3942c00e099SJoe Hershberger #ifdef CONFIG_BOOTP_MAY_FAIL
3952099b9f2SJoe Hershberger char *ethrotate;
3962099b9f2SJoe Hershberger
3972099b9f2SJoe Hershberger ethrotate = env_get("ethrotate");
3982099b9f2SJoe Hershberger if ((ethrotate && strcmp(ethrotate, "no") == 0) ||
3992099b9f2SJoe Hershberger net_restart_wrap) {
400f59be6e8SStephen Warren puts("\nRetry time exceeded\n");
401b977aa80Sbenoit.thebaudeau@advans net_set_state(NETLOOP_FAIL);
4022099b9f2SJoe Hershberger } else
4032099b9f2SJoe Hershberger #endif
4042099b9f2SJoe Hershberger {
405f59be6e8SStephen Warren puts("\nRetry time exceeded; starting again\n");
406bc0571fcSJoe Hershberger net_start_again();
4072099b9f2SJoe Hershberger }
4083861aa5cSwdenk } else {
409f59be6e8SStephen Warren bootp_timeout *= 2;
41092ac8accSThierry Reding if (bootp_timeout > 2000)
41192ac8accSThierry Reding bootp_timeout = 2000;
412bc0571fcSJoe Hershberger net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
4137044c6bbSJoe Hershberger bootp_request();
4143861aa5cSwdenk }
4153861aa5cSwdenk }
4163861aa5cSwdenk
4179ace17c8SIlya Yanok #define put_vci(e, str) \
4189ace17c8SIlya Yanok do { \
4199ace17c8SIlya Yanok size_t vci_strlen = strlen(str); \
4209ace17c8SIlya Yanok *e++ = 60; /* Vendor Class Identifier */ \
4219ace17c8SIlya Yanok *e++ = vci_strlen; \
4229ace17c8SIlya Yanok memcpy(e, str, vci_strlen); \
4239ace17c8SIlya Yanok e += vci_strlen; \
4249ace17c8SIlya Yanok } while (0)
4259ace17c8SIlya Yanok
add_vci(u8 * e)4264570a993SAlexander Graf static u8 *add_vci(u8 *e)
4274570a993SAlexander Graf {
42820898ea9SAlexander Graf char *vci = NULL;
42900caae6dSSimon Glass char *env_vci = env_get("bootp_vci");
43020898ea9SAlexander Graf
4314570a993SAlexander Graf #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_NET_VCI_STRING)
43220898ea9SAlexander Graf vci = CONFIG_SPL_NET_VCI_STRING;
4334570a993SAlexander Graf #elif defined(CONFIG_BOOTP_VCI_STRING)
43420898ea9SAlexander Graf vci = CONFIG_BOOTP_VCI_STRING;
4354570a993SAlexander Graf #endif
4364570a993SAlexander Graf
43720898ea9SAlexander Graf if (env_vci)
43820898ea9SAlexander Graf vci = env_vci;
43920898ea9SAlexander Graf
44020898ea9SAlexander Graf if (vci)
44120898ea9SAlexander Graf put_vci(e, vci);
44220898ea9SAlexander Graf
4434570a993SAlexander Graf return e;
4444570a993SAlexander Graf }
4454570a993SAlexander Graf
4463861aa5cSwdenk /*
4473861aa5cSwdenk * Initialize BOOTP extension fields in the request.
4483861aa5cSwdenk */
449643d1ab2SJon Loeliger #if defined(CONFIG_CMD_DHCP)
dhcp_extended(u8 * e,int message_type,struct in_addr server_ip,struct in_addr requested_ip)450049a95a7SJoe Hershberger static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip,
451049a95a7SJoe Hershberger struct in_addr requested_ip)
4523861aa5cSwdenk {
4533861aa5cSwdenk u8 *start = e;
4543861aa5cSwdenk u8 *cnt;
455bc6fc28bSAlexander Graf #ifdef CONFIG_LIB_UUID
456d2b5d5c4SJason Hobbs char *uuid;
457d2b5d5c4SJason Hobbs #endif
458bc6fc28bSAlexander Graf int clientarch = -1;
459232c150aSwdenk
4601fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_VENDOREX)
4613861aa5cSwdenk u8 *x;
4623861aa5cSwdenk #endif
4631fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_SEND_HOSTNAME)
46477ddac94SWolfgang Denk char *hostname;
465fe389a82Sstroese #endif
4663861aa5cSwdenk
4673861aa5cSwdenk *e++ = 99; /* RFC1048 Magic Cookie */
4683861aa5cSwdenk *e++ = 130;
4693861aa5cSwdenk *e++ = 83;
4703861aa5cSwdenk *e++ = 99;
4713861aa5cSwdenk
4723861aa5cSwdenk *e++ = 53; /* DHCP Message Type */
4733861aa5cSwdenk *e++ = 1;
4743861aa5cSwdenk *e++ = message_type;
4753861aa5cSwdenk
4763861aa5cSwdenk *e++ = 57; /* Maximum DHCP Message Size */
4773861aa5cSwdenk *e++ = 2;
478f8315731SJoe Hershberger *e++ = (576 - 312 + OPT_FIELD_SIZE) >> 8;
479f8315731SJoe Hershberger *e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
4803861aa5cSwdenk
481049a95a7SJoe Hershberger if (server_ip.s_addr) {
482049a95a7SJoe Hershberger int tmp = ntohl(server_ip.s_addr);
4833861aa5cSwdenk
4843861aa5cSwdenk *e++ = 54; /* ServerID */
4853861aa5cSwdenk *e++ = 4;
4863861aa5cSwdenk *e++ = tmp >> 24;
4873861aa5cSwdenk *e++ = tmp >> 16;
4883861aa5cSwdenk *e++ = tmp >> 8;
4893861aa5cSwdenk *e++ = tmp & 0xff;
4903861aa5cSwdenk }
4913861aa5cSwdenk
492049a95a7SJoe Hershberger if (requested_ip.s_addr) {
493049a95a7SJoe Hershberger int tmp = ntohl(requested_ip.s_addr);
4943861aa5cSwdenk
4953861aa5cSwdenk *e++ = 50; /* Requested IP */
4963861aa5cSwdenk *e++ = 4;
4973861aa5cSwdenk *e++ = tmp >> 24;
4983861aa5cSwdenk *e++ = tmp >> 16;
4993861aa5cSwdenk *e++ = tmp >> 8;
5003861aa5cSwdenk *e++ = tmp & 0xff;
5013861aa5cSwdenk }
5021fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_SEND_HOSTNAME)
50300caae6dSSimon Glass hostname = env_get("hostname");
5043090b7e3SJoe Hershberger if (hostname) {
505fe389a82Sstroese int hostnamelen = strlen(hostname);
506232c150aSwdenk
507fe389a82Sstroese *e++ = 12; /* Hostname */
508fe389a82Sstroese *e++ = hostnamelen;
509fe389a82Sstroese memcpy(e, hostname, hostnamelen);
510fe389a82Sstroese e += hostnamelen;
511fe389a82Sstroese }
512fe389a82Sstroese #endif
513fe389a82Sstroese
514bc6fc28bSAlexander Graf #ifdef CONFIG_BOOTP_PXE_CLIENTARCH
515d2b5d5c4SJason Hobbs clientarch = CONFIG_BOOTP_PXE_CLIENTARCH;
516bc6fc28bSAlexander Graf #endif
517bc6fc28bSAlexander Graf
51800caae6dSSimon Glass if (env_get("bootp_arch"))
519bfebc8c9SSimon Glass clientarch = env_get_ulong("bootp_arch", 16, clientarch);
520bc6fc28bSAlexander Graf
521bc6fc28bSAlexander Graf if (clientarch > 0) {
522d2b5d5c4SJason Hobbs *e++ = 93; /* Client System Architecture */
523d2b5d5c4SJason Hobbs *e++ = 2;
524d2b5d5c4SJason Hobbs *e++ = (clientarch >> 8) & 0xff;
525d2b5d5c4SJason Hobbs *e++ = clientarch & 0xff;
526bc6fc28bSAlexander Graf }
527d2b5d5c4SJason Hobbs
528d2b5d5c4SJason Hobbs *e++ = 94; /* Client Network Interface Identifier */
529d2b5d5c4SJason Hobbs *e++ = 3;
530d2b5d5c4SJason Hobbs *e++ = 1; /* type field for UNDI */
531d2b5d5c4SJason Hobbs *e++ = 0; /* major revision */
532d2b5d5c4SJason Hobbs *e++ = 0; /* minor revision */
533d2b5d5c4SJason Hobbs
534bc6fc28bSAlexander Graf #ifdef CONFIG_LIB_UUID
53500caae6dSSimon Glass uuid = env_get("pxeuuid");
536d2b5d5c4SJason Hobbs
537d2b5d5c4SJason Hobbs if (uuid) {
538d2b5d5c4SJason Hobbs if (uuid_str_valid(uuid)) {
539d2b5d5c4SJason Hobbs *e++ = 97; /* Client Machine Identifier */
540d2b5d5c4SJason Hobbs *e++ = 17;
541d2b5d5c4SJason Hobbs *e++ = 0; /* type 0 - UUID */
542d2b5d5c4SJason Hobbs
543d718ded0SPrzemyslaw Marczak uuid_str_to_bin(uuid, e, UUID_STR_FORMAT_STD);
544d2b5d5c4SJason Hobbs e += 16;
545d2b5d5c4SJason Hobbs } else {
546d2b5d5c4SJason Hobbs printf("Invalid pxeuuid: %s\n", uuid);
547d2b5d5c4SJason Hobbs }
548d2b5d5c4SJason Hobbs }
5499ace17c8SIlya Yanok #endif
550d2b5d5c4SJason Hobbs
5514570a993SAlexander Graf e = add_vci(e);
552d2b5d5c4SJason Hobbs
5531fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_VENDOREX)
5543090b7e3SJoe Hershberger x = dhcp_vendorex_prep(e);
5553090b7e3SJoe Hershberger if (x)
5563861aa5cSwdenk return x - start;
5573861aa5cSwdenk #endif
5583861aa5cSwdenk
5593861aa5cSwdenk *e++ = 55; /* Parameter Request List */
5603861aa5cSwdenk cnt = e++; /* Pointer to count of requested items */
5613861aa5cSwdenk *cnt = 0;
5621fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_SUBNETMASK)
5633861aa5cSwdenk *e++ = 1; /* Subnet Mask */
5643861aa5cSwdenk *cnt += 1;
5653861aa5cSwdenk #endif
5661fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_TIMEOFFSET)
567ea287debSwdenk *e++ = 2;
568ea287debSwdenk *cnt += 1;
569ea287debSwdenk #endif
5701fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_GATEWAY)
5713861aa5cSwdenk *e++ = 3; /* Router Option */
5723861aa5cSwdenk *cnt += 1;
5733861aa5cSwdenk #endif
5741fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_DNS)
5753861aa5cSwdenk *e++ = 6; /* DNS Server(s) */
5763861aa5cSwdenk *cnt += 1;
5773861aa5cSwdenk #endif
5781fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_HOSTNAME)
5793861aa5cSwdenk *e++ = 12; /* Hostname */
5803861aa5cSwdenk *cnt += 1;
5813861aa5cSwdenk #endif
5821fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_BOOTFILESIZE)
5833861aa5cSwdenk *e++ = 13; /* Boot File Size */
5843861aa5cSwdenk *cnt += 1;
5853861aa5cSwdenk #endif
5861fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_BOOTPATH)
5873861aa5cSwdenk *e++ = 17; /* Boot path */
5883861aa5cSwdenk *cnt += 1;
5893861aa5cSwdenk #endif
5901fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_NISDOMAIN)
5913861aa5cSwdenk *e++ = 40; /* NIS Domain name request */
5923861aa5cSwdenk *cnt += 1;
5933861aa5cSwdenk #endif
5941fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_NTPSERVER)
595ea287debSwdenk *e++ = 42;
596ea287debSwdenk *cnt += 1;
597ea287debSwdenk #endif
598258ccd68SJason Liu /* no options, so back up to avoid sending an empty request list */
599258ccd68SJason Liu if (*cnt == 0)
600258ccd68SJason Liu e -= 2;
601258ccd68SJason Liu
6023861aa5cSwdenk *e++ = 255; /* End of the list */
6033861aa5cSwdenk
6043861aa5cSwdenk /* Pad to minimal length */
6053861aa5cSwdenk #ifdef CONFIG_DHCP_MIN_EXT_LEN
60621076f61SSimon Glass while ((e - start) < CONFIG_DHCP_MIN_EXT_LEN)
6073861aa5cSwdenk *e++ = 0;
6083861aa5cSwdenk #endif
6093861aa5cSwdenk
6103861aa5cSwdenk return e - start;
6113861aa5cSwdenk }
6123861aa5cSwdenk
613610f2e9cSJon Loeliger #else
6143861aa5cSwdenk /*
6151fe80d79SJon Loeliger * Warning: no field size check - change CONFIG_BOOTP_* at your own risk!
6163861aa5cSwdenk */
bootp_extended(u8 * e)617049a95a7SJoe Hershberger static int bootp_extended(u8 *e)
6183861aa5cSwdenk {
6193861aa5cSwdenk u8 *start = e;
6203861aa5cSwdenk
6213861aa5cSwdenk *e++ = 99; /* RFC1048 Magic Cookie */
6223861aa5cSwdenk *e++ = 130;
6233861aa5cSwdenk *e++ = 83;
6243861aa5cSwdenk *e++ = 99;
6253861aa5cSwdenk
626643d1ab2SJon Loeliger #if defined(CONFIG_CMD_DHCP)
6273861aa5cSwdenk *e++ = 53; /* DHCP Message Type */
6283861aa5cSwdenk *e++ = 1;
6293861aa5cSwdenk *e++ = DHCP_DISCOVER;
6303861aa5cSwdenk
6313861aa5cSwdenk *e++ = 57; /* Maximum DHCP Message Size */
6323861aa5cSwdenk *e++ = 2;
633f8315731SJoe Hershberger *e++ = (576 - 312 + OPT_FIELD_SIZE) >> 16;
634f8315731SJoe Hershberger *e++ = (576 - 312 + OPT_FIELD_SIZE) & 0xff;
635610f2e9cSJon Loeliger #endif
6363861aa5cSwdenk
6374570a993SAlexander Graf add_vci(e);
6389ace17c8SIlya Yanok
6391fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_SUBNETMASK)
6403861aa5cSwdenk *e++ = 1; /* Subnet mask request */
6413861aa5cSwdenk *e++ = 4;
6423861aa5cSwdenk e += 4;
6433861aa5cSwdenk #endif
6443861aa5cSwdenk
6451fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_GATEWAY)
6463861aa5cSwdenk *e++ = 3; /* Default gateway request */
6473861aa5cSwdenk *e++ = 4;
6483861aa5cSwdenk e += 4;
6493861aa5cSwdenk #endif
6503861aa5cSwdenk
6511fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_DNS)
6523861aa5cSwdenk *e++ = 6; /* Domain Name Server */
6533861aa5cSwdenk *e++ = 4;
6543861aa5cSwdenk e += 4;
6553861aa5cSwdenk #endif
6563861aa5cSwdenk
6571fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_HOSTNAME)
6583861aa5cSwdenk *e++ = 12; /* Host name request */
6593861aa5cSwdenk *e++ = 32;
6603861aa5cSwdenk e += 32;
6613861aa5cSwdenk #endif
6623861aa5cSwdenk
6631fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_BOOTFILESIZE)
6643861aa5cSwdenk *e++ = 13; /* Boot file size */
6653861aa5cSwdenk *e++ = 2;
6663861aa5cSwdenk e += 2;
6673861aa5cSwdenk #endif
6683861aa5cSwdenk
6691fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_BOOTPATH)
6703861aa5cSwdenk *e++ = 17; /* Boot path */
6713861aa5cSwdenk *e++ = 32;
6723861aa5cSwdenk e += 32;
6733861aa5cSwdenk #endif
6743861aa5cSwdenk
6751fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_NISDOMAIN)
6763861aa5cSwdenk *e++ = 40; /* NIS Domain name request */
6773861aa5cSwdenk *e++ = 32;
6783861aa5cSwdenk e += 32;
6793861aa5cSwdenk #endif
68009e3a67dSLuuk Paulussen #if defined(CONFIG_BOOTP_NTPSERVER)
68109e3a67dSLuuk Paulussen *e++ = 42;
68209e3a67dSLuuk Paulussen *e++ = 4;
68309e3a67dSLuuk Paulussen e += 4;
68409e3a67dSLuuk Paulussen #endif
6853861aa5cSwdenk
6863861aa5cSwdenk *e++ = 255; /* End of the list */
6873861aa5cSwdenk
688166c409bSAndre Renaud /*
689166c409bSAndre Renaud * If nothing in list, remove it altogether. Some DHCP servers get
690166c409bSAndre Renaud * upset by this minor faux pas and do not respond at all.
691166c409bSAndre Renaud */
692166c409bSAndre Renaud if (e == start + 3) {
693166c409bSAndre Renaud printf("*** Warning: no DHCP options requested\n");
694166c409bSAndre Renaud e -= 3;
695166c409bSAndre Renaud }
696166c409bSAndre Renaud
6973861aa5cSwdenk return e - start;
6983861aa5cSwdenk }
699610f2e9cSJon Loeliger #endif
7003861aa5cSwdenk
bootp_reset(void)7017044c6bbSJoe Hershberger void bootp_reset(void)
702f59be6e8SStephen Warren {
70392ac8accSThierry Reding bootp_num_ids = 0;
7047044c6bbSJoe Hershberger bootp_try = 0;
705f59be6e8SStephen Warren bootp_start = get_timer(0);
70692ac8accSThierry Reding bootp_timeout = 250;
707f59be6e8SStephen Warren }
708f59be6e8SStephen Warren
bootp_request(void)7097044c6bbSJoe Hershberger void bootp_request(void)
7103861aa5cSwdenk {
711db288a96SJoe Hershberger uchar *pkt, *iphdr;
7127044c6bbSJoe Hershberger struct bootp_hdr *bp;
713ae446f56SJoe Hershberger int extlen, pktlen, iplen;
714ae446f56SJoe Hershberger int eth_hdr_size;
715eafc8db0SJoe Hershberger #ifdef CONFIG_BOOTP_RANDOM_DELAY
7168e8d73b4SPavel Machek ulong rand_ms;
717eafc8db0SJoe Hershberger #endif
7185917e7d1SSergey Temerkhanov u32 bootp_id;
719049a95a7SJoe Hershberger struct in_addr zero_ip;
720049a95a7SJoe Hershberger struct in_addr bcast_ip;
72150768f5bSAlexandre Messier char *ep; /* Environment pointer */
7223861aa5cSwdenk
723573f14feSSimon Glass bootstage_mark_name(BOOTSTAGE_ID_BOOTP_START, "bootp_start");
724643d1ab2SJon Loeliger #if defined(CONFIG_CMD_DHCP)
7253861aa5cSwdenk dhcp_state = INIT;
7263861aa5cSwdenk #endif
7273861aa5cSwdenk
72800caae6dSSimon Glass ep = env_get("bootpretryperiod");
72950768f5bSAlexandre Messier if (ep != NULL)
73050768f5bSAlexandre Messier time_taken_max = simple_strtoul(ep, NULL, 10);
73150768f5bSAlexandre Messier else
73250768f5bSAlexandre Messier time_taken_max = TIMEOUT_MS;
73350768f5bSAlexandre Messier
7343861aa5cSwdenk #ifdef CONFIG_BOOTP_RANDOM_DELAY /* Random BOOTP delay */
7357044c6bbSJoe Hershberger if (bootp_try == 0)
736eafc8db0SJoe Hershberger srand_mac();
7373861aa5cSwdenk
7387044c6bbSJoe Hershberger if (bootp_try <= 2) /* Start with max 1024 * 1ms */
7397044c6bbSJoe Hershberger rand_ms = rand() >> (22 - bootp_try);
740eafc8db0SJoe Hershberger else /* After 3rd BOOTP request max 8192 * 1ms */
741eafc8db0SJoe Hershberger rand_ms = rand() >> 19;
7423861aa5cSwdenk
743eafc8db0SJoe Hershberger printf("Random delay: %ld ms...\n", rand_ms);
7448e8d73b4SPavel Machek mdelay(rand_ms);
7453090b7e3SJoe Hershberger
7463861aa5cSwdenk #endif /* CONFIG_BOOTP_RANDOM_DELAY */
7473861aa5cSwdenk
7487044c6bbSJoe Hershberger printf("BOOTP broadcast %d\n", ++bootp_try);
7491203fcceSJoe Hershberger pkt = net_tx_packet;
7503861aa5cSwdenk memset((void *)pkt, 0, PKTSIZE);
7513861aa5cSwdenk
7521203fcceSJoe Hershberger eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
753ae446f56SJoe Hershberger pkt += eth_hdr_size;
7543861aa5cSwdenk
7553861aa5cSwdenk /*
7563090b7e3SJoe Hershberger * Next line results in incorrect packet size being transmitted,
7573090b7e3SJoe Hershberger * resulting in errors in some DHCP servers, reporting missing bytes.
7583090b7e3SJoe Hershberger * Size must be set in packet header after extension length has been
7593090b7e3SJoe Hershberger * determined.
7603861aa5cSwdenk * C. Hallinan, DS4.COM, Inc.
7613861aa5cSwdenk */
7624b11c916SJoe Hershberger /* net_set_udp_header(pkt, 0xFFFFFFFFL, PORT_BOOTPS, PORT_BOOTPC,
7637044c6bbSJoe Hershberger sizeof (struct bootp_hdr)); */
7644b11c916SJoe Hershberger iphdr = pkt; /* We need this later for net_set_udp_header() */
765594c26f8SJoe Hershberger pkt += IP_UDP_HDR_SIZE;
7663861aa5cSwdenk
7677044c6bbSJoe Hershberger bp = (struct bootp_hdr *)pkt;
7683861aa5cSwdenk bp->bp_op = OP_BOOTREQUEST;
7693861aa5cSwdenk bp->bp_htype = HWT_ETHER;
7703861aa5cSwdenk bp->bp_hlen = HWL_ETHER;
7713861aa5cSwdenk bp->bp_hops = 0;
772454d9d3eSStefan Brüns /*
773454d9d3eSStefan Brüns * according to RFC1542, should be 0 on first request, secs since
774454d9d3eSStefan Brüns * first request otherwise
775454d9d3eSStefan Brüns */
776454d9d3eSStefan Brüns bp->bp_secs = htons(get_timer(bootp_start) / 1000);
777049a95a7SJoe Hershberger zero_ip.s_addr = 0;
778049a95a7SJoe Hershberger net_write_ip(&bp->bp_ciaddr, zero_ip);
779049a95a7SJoe Hershberger net_write_ip(&bp->bp_yiaddr, zero_ip);
780049a95a7SJoe Hershberger net_write_ip(&bp->bp_siaddr, zero_ip);
781049a95a7SJoe Hershberger net_write_ip(&bp->bp_giaddr, zero_ip);
7820adb5b76SJoe Hershberger memcpy(bp->bp_chaddr, net_ethaddr, 6);
7831411157dSJoe Hershberger copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
7843861aa5cSwdenk
7853861aa5cSwdenk /* Request additional information from the BOOTP/DHCP server */
786643d1ab2SJon Loeliger #if defined(CONFIG_CMD_DHCP)
787049a95a7SJoe Hershberger extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_DISCOVER, zero_ip,
788049a95a7SJoe Hershberger zero_ip);
7893861aa5cSwdenk #else
790049a95a7SJoe Hershberger extlen = bootp_extended((u8 *)bp->bp_vend);
791610f2e9cSJon Loeliger #endif
7923861aa5cSwdenk
7933861aa5cSwdenk /*
7943861aa5cSwdenk * Bootp ID is the lower 4 bytes of our ethernet address
79549f3bdbbSBartlomiej Sieka * plus the current time in ms.
7963861aa5cSwdenk */
7975917e7d1SSergey Temerkhanov bootp_id = ((u32)net_ethaddr[2] << 24)
7985917e7d1SSergey Temerkhanov | ((u32)net_ethaddr[3] << 16)
7995917e7d1SSergey Temerkhanov | ((u32)net_ethaddr[4] << 8)
8005917e7d1SSergey Temerkhanov | (u32)net_ethaddr[5];
8017044c6bbSJoe Hershberger bootp_id += get_timer(0);
8027044c6bbSJoe Hershberger bootp_id = htonl(bootp_id);
8037044c6bbSJoe Hershberger bootp_add_id(bootp_id);
8045917e7d1SSergey Temerkhanov net_copy_u32(&bp->bp_id, &bootp_id);
8053861aa5cSwdenk
8063861aa5cSwdenk /*
8073861aa5cSwdenk * Calculate proper packet lengths taking into account the
8083861aa5cSwdenk * variable size of the options field
8093861aa5cSwdenk */
810ae446f56SJoe Hershberger iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
811ae446f56SJoe Hershberger pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
812049a95a7SJoe Hershberger bcast_ip.s_addr = 0xFFFFFFFFL;
813049a95a7SJoe Hershberger net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
814bc0571fcSJoe Hershberger net_set_timeout_handler(bootp_timeout, bootp_timeout_handler);
8153861aa5cSwdenk
816643d1ab2SJon Loeliger #if defined(CONFIG_CMD_DHCP)
8173861aa5cSwdenk dhcp_state = SELECTING;
818049a95a7SJoe Hershberger net_set_udp_handler(dhcp_handler);
8193861aa5cSwdenk #else
820049a95a7SJoe Hershberger net_set_udp_handler(bootp_handler);
821610f2e9cSJon Loeliger #endif
8221203fcceSJoe Hershberger net_send_packet(net_tx_packet, pktlen);
8233861aa5cSwdenk }
8243861aa5cSwdenk
825643d1ab2SJon Loeliger #if defined(CONFIG_CMD_DHCP)
dhcp_process_options(uchar * popt,uchar * end)826774c3e05SStefan Brüns static void dhcp_process_options(uchar *popt, uchar *end)
8273861aa5cSwdenk {
8283861aa5cSwdenk int oplen, size;
829d8d8724bSWolfgang Denk #if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
830d8d8724bSWolfgang Denk int *to_ptr;
831d8d8724bSWolfgang Denk #endif
8323861aa5cSwdenk
8333861aa5cSwdenk while (popt < end && *popt != 0xff) {
8343861aa5cSwdenk oplen = *(popt + 1);
8353861aa5cSwdenk switch (*popt) {
836c56eb573SStefan Brüns case 0:
837c56eb573SStefan Brüns oplen = -1; /* Pad omits len byte */
838c56eb573SStefan Brüns break;
8393861aa5cSwdenk case 1:
840049a95a7SJoe Hershberger net_copy_ip(&net_netmask, (popt + 2));
8413861aa5cSwdenk break;
8421fe80d79SJon Loeliger #if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_TIMEOFFSET)
843ea287debSwdenk case 2: /* Time offset */
844bc0571fcSJoe Hershberger to_ptr = &net_ntp_time_offset;
8455917e7d1SSergey Temerkhanov net_copy_u32((u32 *)to_ptr, (u32 *)(popt + 2));
846bc0571fcSJoe Hershberger net_ntp_time_offset = ntohl(net_ntp_time_offset);
847ea287debSwdenk break;
848ea287debSwdenk #endif
8493861aa5cSwdenk case 3:
850049a95a7SJoe Hershberger net_copy_ip(&net_gateway, (popt + 2));
8513861aa5cSwdenk break;
8523861aa5cSwdenk case 6:
853049a95a7SJoe Hershberger net_copy_ip(&net_dns_server, (popt + 2));
8541fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_DNS2)
8553090b7e3SJoe Hershberger if (*(popt + 1) > 4)
856049a95a7SJoe Hershberger net_copy_ip(&net_dns_server2, (popt + 2 + 4));
857fe389a82Sstroese #endif
8583861aa5cSwdenk break;
8593861aa5cSwdenk case 12:
8603090b7e3SJoe Hershberger size = truncate_sz("Host Name",
861586cbe51SJoe Hershberger sizeof(net_hostname), oplen);
862586cbe51SJoe Hershberger memcpy(&net_hostname, popt + 2, size);
863586cbe51SJoe Hershberger net_hostname[size] = 0;
8643861aa5cSwdenk break;
8653861aa5cSwdenk case 15: /* Ignore Domain Name Option */
8663861aa5cSwdenk break;
8673861aa5cSwdenk case 17:
8683090b7e3SJoe Hershberger size = truncate_sz("Root Path",
869586cbe51SJoe Hershberger sizeof(net_root_path), oplen);
870586cbe51SJoe Hershberger memcpy(&net_root_path, popt + 2, size);
871586cbe51SJoe Hershberger net_root_path[size] = 0;
8723861aa5cSwdenk break;
873ee0f60dfSBrian Rzycki case 28: /* Ignore Broadcast Address Option */
874ee0f60dfSBrian Rzycki break;
8751fe80d79SJon Loeliger #if defined(CONFIG_CMD_SNTP) && defined(CONFIG_BOOTP_NTPSERVER)
876ea287debSwdenk case 42: /* NTP server IP */
877049a95a7SJoe Hershberger net_copy_ip(&net_ntp_server, (popt + 2));
878ea287debSwdenk break;
879ea287debSwdenk #endif
8803861aa5cSwdenk case 51:
8815917e7d1SSergey Temerkhanov net_copy_u32(&dhcp_leasetime, (u32 *)(popt + 2));
8823861aa5cSwdenk break;
883ec87b1b3SStefan Brüns case 52:
884ec87b1b3SStefan Brüns dhcp_option_overload = popt[2];
885ec87b1b3SStefan Brüns break;
8863861aa5cSwdenk case 53: /* Ignore Message Type Option */
8873861aa5cSwdenk break;
8883861aa5cSwdenk case 54:
889049a95a7SJoe Hershberger net_copy_ip(&dhcp_server_ip, (popt + 2));
8903861aa5cSwdenk break;
8913861aa5cSwdenk case 58: /* Ignore Renewal Time Option */
8923861aa5cSwdenk break;
8933861aa5cSwdenk case 59: /* Ignore Rebinding Time Option */
8943861aa5cSwdenk break;
8953b2e4fd9SWolfgang Denk case 66: /* Ignore TFTP server name */
8963b2e4fd9SWolfgang Denk break;
897ec87b1b3SStefan Brüns case 67: /* Bootfile option */
898449312c1SAlexander Graf if (!net_boot_file_name_explicit) {
899ec87b1b3SStefan Brüns size = truncate_sz("Bootfile",
900449312c1SAlexander Graf sizeof(net_boot_file_name),
901449312c1SAlexander Graf oplen);
902ec87b1b3SStefan Brüns memcpy(&net_boot_file_name, popt + 2, size);
903ec87b1b3SStefan Brüns net_boot_file_name[size] = 0;
904449312c1SAlexander Graf }
9053b2e4fd9SWolfgang Denk break;
9063861aa5cSwdenk default:
9071fe80d79SJon Loeliger #if defined(CONFIG_BOOTP_VENDOREX)
9083861aa5cSwdenk if (dhcp_vendorex_proc(popt))
9093861aa5cSwdenk break;
9103861aa5cSwdenk #endif
9113090b7e3SJoe Hershberger printf("*** Unhandled DHCP Option in OFFER/ACK:"
9123090b7e3SJoe Hershberger " %d\n", *popt);
9133861aa5cSwdenk break;
9143861aa5cSwdenk }
9153861aa5cSwdenk popt += oplen + 2; /* Process next option */
9163861aa5cSwdenk }
9173861aa5cSwdenk }
9183861aa5cSwdenk
dhcp_packet_process_options(struct bootp_hdr * bp)919774c3e05SStefan Brüns static void dhcp_packet_process_options(struct bootp_hdr *bp)
920774c3e05SStefan Brüns {
921774c3e05SStefan Brüns uchar *popt = (uchar *)&bp->bp_vend[4];
922774c3e05SStefan Brüns uchar *end = popt + BOOTP_HDR_SIZE;
923774c3e05SStefan Brüns
924774c3e05SStefan Brüns if (net_read_u32((u32 *)&bp->bp_vend[0]) != htonl(BOOTP_VENDOR_MAGIC))
925774c3e05SStefan Brüns return;
926774c3e05SStefan Brüns
927774c3e05SStefan Brüns dhcp_option_overload = 0;
928774c3e05SStefan Brüns
929774c3e05SStefan Brüns /*
930774c3e05SStefan Brüns * The 'options' field MUST be interpreted first, 'file' next,
931774c3e05SStefan Brüns * 'sname' last.
932774c3e05SStefan Brüns */
933774c3e05SStefan Brüns dhcp_process_options(popt, end);
934774c3e05SStefan Brüns
935774c3e05SStefan Brüns if (dhcp_option_overload & OVERLOAD_FILE) {
936774c3e05SStefan Brüns popt = (uchar *)bp->bp_file;
937774c3e05SStefan Brüns end = popt + sizeof(bp->bp_file);
938774c3e05SStefan Brüns dhcp_process_options(popt, end);
939774c3e05SStefan Brüns }
940774c3e05SStefan Brüns
941774c3e05SStefan Brüns if (dhcp_option_overload & OVERLOAD_SNAME) {
942774c3e05SStefan Brüns popt = (uchar *)bp->bp_sname;
943774c3e05SStefan Brüns end = popt + sizeof(bp->bp_sname);
944774c3e05SStefan Brüns dhcp_process_options(popt, end);
945774c3e05SStefan Brüns }
946774c3e05SStefan Brüns }
947774c3e05SStefan Brüns
dhcp_message_type(unsigned char * popt)9487044c6bbSJoe Hershberger static int dhcp_message_type(unsigned char *popt)
9493861aa5cSwdenk {
9505917e7d1SSergey Temerkhanov if (net_read_u32((u32 *)popt) != htonl(BOOTP_VENDOR_MAGIC))
9513861aa5cSwdenk return -1;
9523861aa5cSwdenk
9533861aa5cSwdenk popt += 4;
9543861aa5cSwdenk while (*popt != 0xff) {
9553861aa5cSwdenk if (*popt == 53) /* DHCP Message Type */
9563861aa5cSwdenk return *(popt + 2);
957c56eb573SStefan Brüns if (*popt == 0) {
958c56eb573SStefan Brüns /* Pad */
959c56eb573SStefan Brüns popt += 1;
960c56eb573SStefan Brüns } else {
961c56eb573SStefan Brüns /* Scan through all options */
962c56eb573SStefan Brüns popt += *(popt + 1) + 2;
963c56eb573SStefan Brüns }
9643861aa5cSwdenk }
9653861aa5cSwdenk return -1;
9663861aa5cSwdenk }
9673861aa5cSwdenk
dhcp_send_request_packet(struct bootp_hdr * bp_offer)9687044c6bbSJoe Hershberger static void dhcp_send_request_packet(struct bootp_hdr *bp_offer)
9693861aa5cSwdenk {
970db288a96SJoe Hershberger uchar *pkt, *iphdr;
9717044c6bbSJoe Hershberger struct bootp_hdr *bp;
9723861aa5cSwdenk int pktlen, iplen, extlen;
973ae446f56SJoe Hershberger int eth_hdr_size;
974049a95a7SJoe Hershberger struct in_addr offered_ip;
975049a95a7SJoe Hershberger struct in_addr zero_ip;
976049a95a7SJoe Hershberger struct in_addr bcast_ip;
9773861aa5cSwdenk
9787044c6bbSJoe Hershberger debug("dhcp_send_request_packet: Sending DHCPREQUEST\n");
9791203fcceSJoe Hershberger pkt = net_tx_packet;
9803861aa5cSwdenk memset((void *)pkt, 0, PKTSIZE);
9813861aa5cSwdenk
9821203fcceSJoe Hershberger eth_hdr_size = net_set_ether(pkt, net_bcast_ethaddr, PROT_IP);
983ae446f56SJoe Hershberger pkt += eth_hdr_size;
9843861aa5cSwdenk
9853861aa5cSwdenk iphdr = pkt; /* We'll need this later to set proper pkt size */
986594c26f8SJoe Hershberger pkt += IP_UDP_HDR_SIZE;
9873861aa5cSwdenk
9887044c6bbSJoe Hershberger bp = (struct bootp_hdr *)pkt;
9893861aa5cSwdenk bp->bp_op = OP_BOOTREQUEST;
9903861aa5cSwdenk bp->bp_htype = HWT_ETHER;
9913861aa5cSwdenk bp->bp_hlen = HWL_ETHER;
9923861aa5cSwdenk bp->bp_hops = 0;
993454d9d3eSStefan Brüns bp->bp_secs = htons(get_timer(bootp_start) / 1000);
9943090b7e3SJoe Hershberger /* Do not set the client IP, your IP, or server IP yet, since it
9953090b7e3SJoe Hershberger * hasn't been ACK'ed by the server yet */
996e5c794e4SJustin Flammia
997d82718feSWolfgang Denk /*
998d82718feSWolfgang Denk * RFC3046 requires Relay Agents to discard packets with
999d82718feSWolfgang Denk * nonzero and offered giaddr
1000d82718feSWolfgang Denk */
1001049a95a7SJoe Hershberger zero_ip.s_addr = 0;
1002049a95a7SJoe Hershberger net_write_ip(&bp->bp_giaddr, zero_ip);
1003d82718feSWolfgang Denk
10040adb5b76SJoe Hershberger memcpy(bp->bp_chaddr, net_ethaddr, 6);
1005b2b7fbc3SAlexandre Messier copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file));
10063861aa5cSwdenk
10073861aa5cSwdenk /*
10083861aa5cSwdenk * ID is the id of the OFFER packet
10093861aa5cSwdenk */
10103861aa5cSwdenk
10115917e7d1SSergey Temerkhanov net_copy_u32(&bp->bp_id, &bp_offer->bp_id);
10123861aa5cSwdenk
10133861aa5cSwdenk /*
10143861aa5cSwdenk * Copy options from OFFER packet if present
10153861aa5cSwdenk */
1016e5c794e4SJustin Flammia
1017e5c794e4SJustin Flammia /* Copy offered IP into the parameters request list */
1018049a95a7SJoe Hershberger net_copy_ip(&offered_ip, &bp_offer->bp_yiaddr);
1019049a95a7SJoe Hershberger extlen = dhcp_extended((u8 *)bp->bp_vend, DHCP_REQUEST,
1020049a95a7SJoe Hershberger dhcp_server_ip, offered_ip);
10213861aa5cSwdenk
1022ae446f56SJoe Hershberger iplen = BOOTP_HDR_SIZE - OPT_FIELD_SIZE + extlen;
1023ae446f56SJoe Hershberger pktlen = eth_hdr_size + IP_UDP_HDR_SIZE + iplen;
1024049a95a7SJoe Hershberger bcast_ip.s_addr = 0xFFFFFFFFL;
1025049a95a7SJoe Hershberger net_set_udp_header(iphdr, bcast_ip, PORT_BOOTPS, PORT_BOOTPC, iplen);
10263861aa5cSwdenk
1027d9a2f416SAras Vaichas #ifdef CONFIG_BOOTP_DHCP_REQUEST_DELAY
1028d9a2f416SAras Vaichas udelay(CONFIG_BOOTP_DHCP_REQUEST_DELAY);
1029d9a2f416SAras Vaichas #endif /* CONFIG_BOOTP_DHCP_REQUEST_DELAY */
1030f9623229SJoe Hershberger debug("Transmitting DHCPREQUEST packet: len = %d\n", pktlen);
10311203fcceSJoe Hershberger net_send_packet(net_tx_packet, pktlen);
10323861aa5cSwdenk }
10333861aa5cSwdenk
10343861aa5cSwdenk /*
10353861aa5cSwdenk * Handle DHCP received packets.
10363861aa5cSwdenk */
dhcp_handler(uchar * pkt,unsigned dest,struct in_addr sip,unsigned src,unsigned len)1037049a95a7SJoe Hershberger static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
1038049a95a7SJoe Hershberger unsigned src, unsigned len)
10393861aa5cSwdenk {
10407044c6bbSJoe Hershberger struct bootp_hdr *bp = (struct bootp_hdr *)pkt;
10413861aa5cSwdenk
10423861aa5cSwdenk debug("DHCPHandler: got packet: (src=%d, dst=%d, len=%d) state: %d\n",
10433861aa5cSwdenk src, dest, len, dhcp_state);
10443861aa5cSwdenk
10453090b7e3SJoe Hershberger /* Filter out pkts we don't want */
1046867d6ae2SStefan Brüns if (check_reply_packet(pkt, dest, src, len))
10473861aa5cSwdenk return;
10483861aa5cSwdenk
10493090b7e3SJoe Hershberger debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: "
10503090b7e3SJoe Hershberger "%d\n", src, dest, len, dhcp_state);
10513861aa5cSwdenk
105244c42dd4SPeng Fan if (net_read_ip(&bp->bp_yiaddr).s_addr == 0)
105344c42dd4SPeng Fan return;
105444c42dd4SPeng Fan
10553861aa5cSwdenk switch (dhcp_state) {
10563861aa5cSwdenk case SELECTING:
10573861aa5cSwdenk /*
10583861aa5cSwdenk * Wait an appropriate time for any potential DHCPOFFER packets
10593090b7e3SJoe Hershberger * to arrive. Then select one, and generate DHCPREQUEST
10603090b7e3SJoe Hershberger * response. If filename is in format we recognize, assume it
10613090b7e3SJoe Hershberger * is a valid OFFER from a server we want.
10623861aa5cSwdenk */
10633861aa5cSwdenk debug("DHCP: state=SELECTING bp_file: \"%s\"\n", bp->bp_file);
10646d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_BOOTFILE_PREFIX
10653861aa5cSwdenk if (strncmp(bp->bp_file,
10666d0f6bcfSJean-Christophe PLAGNIOL-VILLARD CONFIG_SYS_BOOTFILE_PREFIX,
10676d0f6bcfSJean-Christophe PLAGNIOL-VILLARD strlen(CONFIG_SYS_BOOTFILE_PREFIX)) == 0) {
10686d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_BOOTFILE_PREFIX */
1069774c3e05SStefan Brüns dhcp_packet_process_options(bp);
10700efe1bcfSAlexander Graf efi_net_set_dhcp_ack(pkt, len);
10713861aa5cSwdenk
10723861aa5cSwdenk debug("TRANSITIONING TO REQUESTING STATE\n");
10733861aa5cSwdenk dhcp_state = REQUESTING;
1074759a51b4Sstroese
1075bc0571fcSJoe Hershberger net_set_timeout_handler(5000, bootp_timeout_handler);
10767044c6bbSJoe Hershberger dhcp_send_request_packet(bp);
10776d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_SYS_BOOTFILE_PREFIX
10783861aa5cSwdenk }
10796d0f6bcfSJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_SYS_BOOTFILE_PREFIX */
10803861aa5cSwdenk
10813861aa5cSwdenk return;
10823861aa5cSwdenk break;
10833861aa5cSwdenk case REQUESTING:
10843861aa5cSwdenk debug("DHCP State: REQUESTING\n");
10853861aa5cSwdenk
10867044c6bbSJoe Hershberger if (dhcp_message_type((u8 *)bp->bp_vend) == DHCP_ACK) {
1087774c3e05SStefan Brüns dhcp_packet_process_options(bp);
10883090b7e3SJoe Hershberger /* Store net params from reply */
10897044c6bbSJoe Hershberger store_net_params(bp);
10903861aa5cSwdenk dhcp_state = BOUND;
109192ac8accSThierry Reding printf("DHCP client bound to address %pI4 (%lu ms)\n",
1092049a95a7SJoe Hershberger &net_ip, get_timer(bootp_start));
10934f28c9b1SStefan Brüns net_set_timeout_handler(0, (thand_f *)0);
1094573f14feSSimon Glass bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP,
1095573f14feSSimon Glass "bootp_stop");
10963861aa5cSwdenk
1097e4a3d57dSSimon Glass net_auto_load();
10983861aa5cSwdenk return;
10993861aa5cSwdenk }
11003861aa5cSwdenk break;
110151dfe138SRemy Bohmer case BOUND:
110251dfe138SRemy Bohmer /* DHCP client bound to address */
110351dfe138SRemy Bohmer break;
11043861aa5cSwdenk default:
11054b9206edSwdenk puts("DHCP: INVALID STATE\n");
11063861aa5cSwdenk break;
11073861aa5cSwdenk }
11083861aa5cSwdenk }
11093861aa5cSwdenk
dhcp_request(void)11107044c6bbSJoe Hershberger void dhcp_request(void)
11113861aa5cSwdenk {
11127044c6bbSJoe Hershberger bootp_request();
11133861aa5cSwdenk }
1114992742a5SWolfgang Denk #endif /* CONFIG_CMD_DHCP */
1115