X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=net%2Fbootp.c;h=9a2b512e4a72b6aab102a8f71c9067c510d045a3;hb=8edaf34cfe8b672a4b7e129e8b7c736e12ead24a;hp=b2f8ad4ded932ddfdb2aefc86f803e2d4f0d894f;hpb=346969584be509b444dd1ba0db31ca7adb47575b;p=oweals%2Fu-boot.git diff --git a/net/bootp.c b/net/bootp.c index b2f8ad4ded..9a2b512e4a 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -10,11 +10,11 @@ #include #include +#include #include #include #include "bootp.h" -#include "nfs.h" -#ifdef CONFIG_STATUS_LED +#ifdef CONFIG_LED_STATUS #include #endif #ifdef CONFIG_BOOTP_RANDOM_DELAY @@ -60,10 +60,15 @@ char net_nis_domain[32] = {0,}; /* Our NIS domain */ 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 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); @@ -109,7 +114,8 @@ static bool bootp_match_id(ulong id) 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; @@ -118,11 +124,7 @@ static int check_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len) 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; @@ -130,6 +132,8 @@ static int check_packet(uchar *pkt, unsigned dest, unsigned src, unsigned len) retval = -5; 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); @@ -143,15 +147,26 @@ static void store_net_params(struct bootp_hdr *bp) { #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); @@ -160,7 +175,7 @@ static void store_net_params(struct bootp_hdr *bp) * 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); } @@ -324,7 +339,7 @@ static void bootp_process_vendor(u8 *ext, int size) 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 } @@ -343,14 +358,14 @@ static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip, 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 */ @@ -375,14 +390,21 @@ static void bootp_timeout_handler(void) { 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) @@ -401,6 +423,26 @@ static void bootp_timeout_handler(void) 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. */ @@ -410,10 +452,10 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, { 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; @@ -458,7 +500,7 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, *e++ = tmp & 0xff; } #if defined(CONFIG_BOOTP_SEND_HOSTNAME) - hostname = getenv("hostname"); + hostname = env_get("hostname"); if (hostname) { int hostnamelen = strlen(hostname); @@ -469,12 +511,19 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, } #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; @@ -482,7 +531,8 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, *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)) { @@ -498,9 +548,7 @@ static int dhcp_extended(u8 *e, int message_type, struct in_addr server_ip, } #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); @@ -586,14 +634,7 @@ static int bootp_extended(u8 *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 */ @@ -644,6 +685,15 @@ static int bootp_extended(u8 *e) *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 @@ -668,12 +718,19 @@ void bootp_request(void) 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(); @@ -712,7 +769,11 @@ void bootp_request(void) 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); @@ -762,9 +823,8 @@ void bootp_request(void) } #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; @@ -773,6 +833,9 @@ static void dhcp_process_options(uchar *popt, struct bootp_hdr *bp) 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; @@ -817,6 +880,9 @@ static void dhcp_process_options(uchar *popt, struct bootp_hdr *bp) case 51: net_copy_u32(&dhcp_leasetime, (u32 *)(popt + 2)); break; + case 52: + dhcp_option_overload = popt[2]; + break; case 53: /* Ignore Message Type Option */ break; case 54: @@ -828,30 +894,13 @@ static void dhcp_process_options(uchar *popt, struct bootp_hdr *bp) 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: @@ -867,6 +916,35 @@ static void dhcp_process_options(uchar *popt, struct bootp_hdr *bp) } } +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_u32((u32 *)popt) != htonl(BOOTP_VENDOR_MAGIC)) @@ -876,7 +954,13 @@ static int dhcp_message_type(unsigned char *popt) 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; } @@ -906,7 +990,7 @@ static void dhcp_send_request_packet(struct bootp_hdr *bp_offer) 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 */ @@ -918,6 +1002,7 @@ static void dhcp_send_request_packet(struct bootp_hdr *bp_offer) 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 @@ -958,12 +1043,15 @@ static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip, 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: /* @@ -978,14 +1066,12 @@ static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip, 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_u32((u32 *)&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 @@ -998,14 +1084,13 @@ static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip, debug("DHCP State: REQUESTING\n"); if (dhcp_message_type((u8 *)bp->bp_vend) == DHCP_ACK) { - if (net_read_u32((u32 *)&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");