]> git.sur5r.net Git - u-boot/blobdiff - net/bootp.c
dm: implement a DMA uclass
[u-boot] / net / bootp.c
index fbdc7cb3a32899a8c3e6bde3b39a0b216c7da9b9..f2978a23ce5aaaa8074ae6bdeac950363edeaca0 100644 (file)
@@ -64,6 +64,9 @@ char net_root_path[64] = {0,}; /* Our bootpath */
 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 +112,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 +122,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;
@@ -149,9 +149,14 @@ static void store_net_params(struct bootp_hdr *bp)
                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)) {
                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);
 
@@ -343,7 +348,7 @@ 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;
 
        /*
@@ -714,7 +719,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);
@@ -764,9 +773,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;
@@ -775,6 +783,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;
@@ -819,6 +830,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:
@@ -830,31 +844,11 @@ 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 */
+                       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:
 #if defined(CONFIG_BOOTP_VENDOREX)
@@ -869,6 +863,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))
@@ -878,7 +901,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;
 }
@@ -908,7 +937,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 */
 
@@ -920,6 +949,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
@@ -960,12 +990,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:
                /*
@@ -980,14 +1013,11 @@ 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);
 
                        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
@@ -1000,14 +1030,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");