Merge "Replace bootp with dhcp."
diff --git a/src/netboot/Makefile.inc b/src/netboot/Makefile.inc
index b584d2f..5ff6533 100644
--- a/src/netboot/Makefile.inc
+++ b/src/netboot/Makefile.inc
@@ -17,7 +17,7 @@
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
-netboot-y += bootp.c
+netboot-y += dhcp.c
netboot-y += main.c
netboot-y += tftp.c
diff --git a/src/netboot/bootp.c b/src/netboot/bootp.c
deleted file mode 100644
index 137e7c7..0000000
--- a/src/netboot/bootp.c
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but without any warranty; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <assert.h>
-#include <endian.h>
-#include <libpayload.h>
-
-#include "drivers/net/net.h"
-#include "net/net.h"
-#include "net/uip.h"
-#include "net/uip_arp.h"
-#include "net/uip-udp-packet.h"
-#include "netboot/bootp.h"
-
-static BootpPacket *bootp_reply;
-static int bootp_reply_ready;
-static BootpPacket *bootp_request;
-
-static void bootp_options(uint8_t *options, int bytes);
-
-static void bootp_callback(void)
-{
- // Check that it's the right port. If it isn't, some other connection
- // is open and we got their packet, and that's a bug on our end.
- assert(ntohw(uip_udp_conn->lport) == BootpClientPort);
-
- // If there isn't any or enough data, ignore the packet.
- if (!uip_newdata())
- return;
- if (uip_datalen() < sizeof(BootpPacket))
- return;
-
- // Copy the packet out to ensure alignment, etc.
- memcpy(bootp_reply, uip_appdata, sizeof(BootpPacket));
-
- // Check for problems in the reply.
- if (bootp_reply->opcode != BootpReply ||
- bootp_reply->hw_length != bootp_request->hw_length ||
- memcmp(bootp_reply->client_hw_addr,
- bootp_request->client_hw_addr,
- bootp_request->hw_length) ||
- (bootp_reply->transaction_id !=
- bootp_request->transaction_id))
- return;
-
- // Everything checks out. We have a valid reply.
- bootp_reply_ready = 1;
-}
-
-int bootp(uip_ipaddr_t *server_ip, const char **bootfile)
-{
- BootpPacket request, reply;
-
- // Prepare the bootp request packet.
- memset(&request, 0, sizeof(request));
- request.opcode = BootpRequest;
- request.hw_type = BootpEthernet;
- request.hw_length = sizeof(uip_ethaddr);
- request.hops = 0;
- request.transaction_id = rand();
- request.seconds = htonw(100);
- assert(sizeof(uip_ethaddr) < sizeof(request.client_hw_addr));
- memcpy(request.client_hw_addr, &uip_ethaddr, sizeof(uip_ethaddr));
- memcpy(request.options, BootpCookie, sizeof(BootpCookie));
- // Truncate the option list.
- request.options[sizeof(BootpCookie)] = BootpTagEndOfList;
-
- // Set up the UDP connection.
- uip_ipaddr_t addr;
- uip_ipaddr(&addr, 255,255,255,255);
- struct uip_udp_conn *conn = uip_udp_new(&addr, htonw(BootpServerPort));
- if (!conn) {
- printf("Failed to set up UDP connection.\n");
- return -1;
- }
- uip_udp_bind(conn, htonw(BootpClientPort));
-
- // Send the request.
- printf("Sending bootp request... ");
- uip_udp_packet_send(conn, &request, sizeof(request));
- printf("done.\n");
-
- // Prepare for the reply.
- printf("Waiting for reply... ");
- bootp_reply = &reply;
- bootp_request = &request;
- bootp_reply_ready = 0;
-
- // Poll network driver until we get a reply. Resend periodically.
- net_set_callback(&bootp_callback);
- for (;;) {
- net_poll();
- if (bootp_reply_ready)
- break;
- // No response, try again.
- uip_udp_packet_send(conn, &request, sizeof(request));
- }
- uip_udp_remove(conn);
- net_set_callback(NULL);
- printf("done.\n");
-
- // Get the stuff we wanted out of the reply.
- int bootfile_size = sizeof(reply.bootfile_name) + 1;
- char *file = malloc(bootfile_size);
- if (!file) {
- printf("Failed to allocate %d bytes for bootfile name.\n",
- bootfile_size);
- return -1;
- }
- file[bootfile_size - 1] = 0;
- memcpy(file, reply.bootfile_name, sizeof(reply.bootfile_name));
- *bootfile = file;
- uip_ipaddr(server_ip, reply.server_ip >> 0, reply.server_ip >> 8,
- reply.server_ip >> 16, reply.server_ip >> 24);
-
- // Apply the new network info.
- uip_ipaddr_t my_ip;
- uip_ipaddr(&my_ip, reply.your_ip >> 0, reply.your_ip >> 8,
- reply.your_ip >> 16, reply.your_ip >> 24);
- uip_sethostaddr(&my_ip);
-
- bootp_options(reply.options, sizeof(reply.options));
-
- return 0;
-}
-
-static void bootp_options(uint8_t *options, int bytes)
-{
- if (bytes < sizeof(BootpCookie) ||
- memcmp(options, BootpCookie, sizeof(BootpCookie)))
- return;
-
- int pos = sizeof(BootpCookie);
-
- while (pos < bytes) {
- uint8_t tag = options[pos++];
- if (pos >= bytes)
- return;
- uint8_t length = options[pos++];
- if (pos >= bytes)
- return;
- uint8_t *value = &options[pos];
- pos += length;
- if (pos >= bytes)
- return;
-
- switch (tag) {
- case BootpTagPadding:
- case BootpTagTimeOffset:
- case BootpTagTimeServers:
- case BootpTagDnsServers:
- case BootpTagPrintServers:
- case BootpTagHostName:
- case BootpTagFileSize:
- break;
- case BootpTagSubnetMask:
- if (length < 4) {
- printf("Truncated subnet mask.\n");
- } else {
- uip_ipaddr_t netmask;
- uip_ipaddr(&netmask, value[0], value[1],
- value[2], value[3]);
- uip_setnetmask(&netmask);
- }
- break;
- case BootpTagDefaultRouters:
- if (length < 4) {
- printf("Truncated routers.\n");
- } else {
- uip_ipaddr_t router;
- uip_ipaddr(&router, value[0], value[1],
- value[2], value[3]);
- uip_setdraddr(&router);
- }
- break;
- case BootpTagEndOfList:
- return;
- }
- }
-}
diff --git a/src/netboot/bootp.h b/src/netboot/bootp.h
deleted file mode 100644
index fcc2722..0000000
--- a/src/netboot/bootp.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but without any warranty; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#ifndef __NETBOOT_BOOTP_H__
-#define __NETBOOT_BOOTP_H__
-
-#include <stdint.h>
-
-#include "net/uip.h"
-
-typedef enum BootpOpcode
-{
- BootpRequest = 1,
- BootpReply = 2
-} BootpOpcode;
-
-typedef enum BootpHardwareType
-{
- BootpEthernet = 1
-} BootpHardwareType;
-
-static const uint16_t BootpClientPort = 68;
-static const uint16_t BootpServerPort = 67;
-
-typedef struct __attribute__((packed)) BootpPacket
-{
- uint8_t opcode;
- uint8_t hw_type;
- uint8_t hw_length;
- uint8_t hops;
- uint32_t transaction_id;
- uint16_t seconds;
- uint16_t unused;
- uint32_t client_ip;
- uint32_t your_ip;
- uint32_t server_ip;
- uint32_t gateway_ip;
- uint8_t client_hw_addr[16];
- uint8_t server_name[64];
- uint8_t bootfile_name[128];
- uint8_t options[64];
-} BootpPacket;
-
-static const uint8_t BootpCookie[] = { 99, 130, 83, 99 };
-
-typedef enum BootpTags {
- BootpTagPadding = 0,
- BootpTagSubnetMask = 1,
- BootpTagTimeOffset = 2,
- BootpTagDefaultRouters = 3,
- BootpTagTimeServers = 4,
- BootpTagDnsServers = 6,
- BootpTagPrintServers = 9,
- BootpTagHostName = 12,
- BootpTagFileSize = 13,
- BootpTagEndOfList = 255
-} BootpTags;
-
-int bootp(uip_ipaddr_t *server_ip, const char **bootfile);
-
-#endif /* __NETBOOT_BOOTP_H__ */
diff --git a/src/netboot/dhcp.c b/src/netboot/dhcp.c
new file mode 100644
index 0000000..0954e4a
--- /dev/null
+++ b/src/netboot/dhcp.c
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <endian.h>
+#include <libpayload.h>
+
+#include "drivers/net/net.h"
+#include "net/net.h"
+#include "net/uip.h"
+#include "net/uip_arp.h"
+#include "net/uip-udp-packet.h"
+#include "netboot/dhcp.h"
+
+typedef enum DhcpOpcode
+{
+ DhcpRequestOp = 1,
+ DhcpReplyOp = 2
+} DhcpOpcode;
+
+typedef enum DhcpHardwareType
+{
+ DhcpEthernet = 1
+} DhcpHardwareType;
+
+static const uint16_t DhcpServerPort = 67;
+static const uint16_t DhcpClientPort = 68;
+
+typedef struct __attribute__((packed)) DhcpPacket
+{
+ uint8_t opcode;
+ uint8_t hw_type;
+ uint8_t hw_length;
+ uint8_t hops;
+ uint32_t transaction_id;
+ uint16_t seconds;
+ uint16_t flags;
+ uint32_t client_ip;
+ uint32_t your_ip;
+ uint32_t server_ip;
+ uint32_t gateway_ip;
+ uint8_t client_hw_addr[16];
+ uint8_t server_name[64];
+ uint8_t bootfile_name[128];
+ uint32_t cookie;
+ uint8_t options[308];
+} DhcpPacket;
+
+static const unsigned int DhcpMinPacketSize = 576;
+static const unsigned int DhcpMaxPacketSize =
+ sizeof(DhcpPacket) + UIP_IPH_LEN + UIP_UDPH_LEN;
+
+static const uint8_t DhcpCookie[] = { 99, 130, 83, 99 };
+
+typedef enum DhcpTags {
+ // "Vendor extensions".
+ DhcpTagPadding = 0,
+ DhcpTagSubnetMask = 1,
+ DhcpTagTimeOffset = 2,
+ DhcpTagDefaultRouter = 3,
+ DhcpTagTimeServer = 4,
+ DhcpTagDnsServer = 6,
+ DhcpTagLogServer = 7,
+ DhcpTagCookieServer = 8,
+ DhcpTagPrintServer = 9,
+ DhcpTagImpressServer = 10,
+ DhcpTagResourceLocationServer = 11,
+ DhcpTagHostName = 12,
+ DhcpTagBootFileSize = 13,
+ DhcpTagMeritDumpFile = 14,
+ DhcpTagDomainName = 15,
+ DhcpTagSwapServer = 16,
+ DhcpTagRootPath = 17,
+ DhcpTagExtensionsPath = 18,
+
+ // IP layer paramters per host.
+ DhcpTagIpForwarding = 19,
+ DhcpTagNonLocalSourceRouting = 20,
+ DhcpTagPolicyFilter = 21,
+ DhcpTagMaximumDatagramSize = 22,
+ DhcpTagDefaultTimeToLive = 23,
+ DhcpTagPathMtuAgingTimeout = 24,
+ DhcpTagPathMtuPlateauTable = 25,
+
+ // IP later parameters per interface.
+ DhcpTagInterfaceMtu = 26,
+ DhcpTagAllSubnetsAreLocal = 27,
+ DhcpTagBroadcastAddress = 28,
+ DhcpTagPerformMaskDiscovery = 29,
+ DhcpTagMaskSupplier = 30,
+ DhcpTagPerformRouterDiscovery = 31,
+ DhcpTagRouterSolicitationAddress = 32,
+ DhcpTagStaticRouteOption = 33,
+
+ // Link layer paramters per interface.
+ DhcpTagTrailerEncapsulation = 34,
+ DhcpTagArpCacheTimeout = 35,
+ DhcpTagEthernetEncapsulation = 36,
+
+ // Tcp parameters.
+ DhcpTagTcpDefaultTtl = 37,
+ DhcpTagTcpKeepaliveInterval = 38,
+ DhcpTagTcpKeepaliveGarbage = 39,
+
+ // Application and service parameters.
+ DhcpTagNetworkInformationServiceDomain = 40,
+ DhcpTagNetworkInformationServers = 41,
+ DhcpTagNetworkTimeProtocolServers = 42,
+ DhcpTagVendorSpecificInformation = 43,
+ DhcpTagNetbiosOverTcpIpNameServer = 44,
+ DhcpTagNetbiosOverTcpIpDatagramDistributionServer = 45,
+ DhcpTagNetbiosOverTcpIpNodeType = 46,
+ DhcpTagNetbiosOverTcpIpScope = 47,
+ DhcpTagXWindowSystemFontServer = 48,
+ DhcpTagXWindowSystemDisplayManager = 49,
+
+ // Dhcp extensions.
+ DhcpTagRequestedIpAddress = 50,
+ DhcpTagIpAddressLeaseTime = 51,
+ DhcpTagOptionOverload = 52,
+ DhcpTagMessageType = 53,
+ DhcpTagServerIdentifier = 54,
+ DhcpTagParameterRequestList = 55,
+ DhcpTagMessage = 56,
+ DhcpTagMaximumDhcpMessageSize = 57,
+ DhcpTagRenewalTimeValue = 58,
+ DhcpTagRebindTimeValue = 59,
+ DhcpTagClassIdentifier = 60,
+ DhcpTagClientIdentifier = 61,
+
+ DhcpTagEndOfList = 255
+} DhcpTags;
+
+typedef enum DhcpState
+{
+ DhcpInit,
+ DhcpRequesting,
+ DhcpBound
+} DhcpState;
+
+typedef enum DhcpMessageType
+{
+ DhcpNoMessageType = 0,
+ DhcpDiscover = 1,
+ DhcpOffer = 2,
+ DhcpRequest = 3,
+ DhcpDecline = 4,
+ DhcpAck = 5,
+ DhcpNak = 6,
+ DhcpRelease = 7
+} DhcpMessageType;
+
+enum {
+ OptionOverloadNone = 0,
+ OptionOverloadFile = 1,
+ OptionOverloadSname = 2,
+ OptionOverloadBoth = 3
+};
+
+// Used in dhcp_callback().
+static DhcpState dhcp_state = DhcpInit;
+static DhcpPacket *dhcp_in;
+static int dhcp_in_ready;
+static DhcpPacket *dhcp_out;
+
+typedef int (*DhcpOptionFunc)(uint8_t tag, uint8_t length, uint8_t *value,
+ void *data);
+
+static int dhcp_process_options(DhcpPacket *packet, int overload,
+ DhcpOptionFunc func, void *data)
+{
+ uint8_t *options;
+ int size;
+ switch (overload) {
+ case OptionOverloadNone:
+ options = packet->options;
+ size = sizeof(packet->options);
+ break;
+ case OptionOverloadFile:
+ options = packet->bootfile_name;
+ size = sizeof(packet->bootfile_name);
+ break;
+ case OptionOverloadSname:
+ options = packet->server_name;
+ size = sizeof(packet->server_name);
+ break;
+ case OptionOverloadBoth:
+ options = packet->server_name;
+ size = sizeof(packet->server_name) +
+ sizeof(packet->bootfile_name);
+ break;
+ default:
+ return 1;
+ }
+
+ int pos = 0;
+
+ int new_overload = 0;
+
+ while (pos < size) {
+ uint8_t tag = options[pos++];
+ if (pos >= size)
+ return 1;
+ uint8_t length = options[pos++];
+ if (pos >= size)
+ return 1;
+ uint8_t *value = &options[pos];
+ pos += length;
+ if (pos >= size)
+ return 1;
+
+ // Handle "end of list" and "option overload" options.
+ if (tag == DhcpTagEndOfList)
+ break;
+ if (tag == DhcpTagOptionOverload) {
+ if (length < 1)
+ return 1;
+ new_overload = *value;
+ continue;
+ }
+
+ // Otherwise, use the provided callback.
+ assert(func);
+ if (func(tag, length, value, data))
+ return 1;
+ }
+
+ if (!overload && new_overload)
+ return dhcp_process_options(packet, new_overload, func, data);
+
+ return 0;
+}
+
+static int dhcp_get_type(uint8_t tag, uint8_t length, uint8_t *value,
+ void *data)
+{
+ if (tag != DhcpTagMessageType)
+ return 0;
+ if (length < 1)
+ return 1;
+
+ DhcpMessageType *type = (DhcpMessageType *)data;
+ *type = (DhcpMessageType)*value;
+
+ return 0;
+}
+
+static int dhcp_get_server(uint8_t tag, uint8_t length, uint8_t *value,
+ void *data)
+{
+ if (tag != DhcpTagServerIdentifier)
+ return 0;
+ if (length < sizeof(uint32_t))
+ return 1;
+
+ memcpy(data, value, sizeof(uint32_t));
+
+ return 0;
+}
+
+static int dhcp_apply_options(uint8_t tag, uint8_t length, uint8_t *value,
+ void *data)
+{
+ switch (tag) {
+ case DhcpTagSubnetMask:
+ if (length < 4) {
+ printf("Truncated subnet mask.\n");
+ return 1;
+ } else {
+ uip_ipaddr_t netmask;
+ uip_ipaddr(&netmask, value[0], value[1],
+ value[2], value[3]);
+ uip_setnetmask(&netmask);
+ return 0;
+ }
+ case DhcpTagDefaultRouter:
+ if (length < 4) {
+ printf("Truncated routers.\n");
+ return 1;
+ } else {
+ uip_ipaddr_t router;
+ uip_ipaddr(&router, value[0], value[1],
+ value[2], value[3]);
+ uip_setdraddr(&router);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static void dhcp_callback(void)
+{
+ // Check that it's the right port. If it isn't, some other connection
+ // is open and we got their packet, and that's a bug on our end.
+ assert(ntohw(uip_udp_conn->lport) == DhcpClientPort);
+
+ // If there isn't any data, ignore the packet.
+ if (!uip_newdata())
+ return;
+
+ // Copy the packet out to ensure alignment, etc.
+ memset(dhcp_in, 0, sizeof(DhcpPacket));
+ memcpy(dhcp_in, uip_appdata, uip_datalen());
+
+ // Check for problems in the reply.
+ if (dhcp_in->opcode != DhcpReplyOp ||
+ dhcp_in->hw_length != dhcp_out->hw_length ||
+ memcmp(dhcp_in->client_hw_addr,
+ dhcp_out->client_hw_addr,
+ dhcp_out->hw_length) ||
+ (dhcp_in->transaction_id != dhcp_out->transaction_id))
+ return;
+
+ if (memcmp(&dhcp_in->cookie, DhcpCookie, sizeof(DhcpCookie)))
+ return;
+
+ DhcpMessageType type = DhcpNoMessageType;
+ if (dhcp_process_options(dhcp_in, OptionOverloadNone, &dhcp_get_type,
+ &type))
+ return;
+
+ switch (dhcp_state) {
+ case DhcpInit:
+ if (type != DhcpOffer)
+ return;
+ break;
+ case DhcpRequesting:
+ if (type != DhcpAck && type != DhcpNak)
+ return;
+ break;
+ case DhcpBound:
+ // We shouldn't get any more packets once we're bound.
+ break;
+ }
+
+ // Everything checks out. We have a valid reply.
+ dhcp_in_ready = 1;
+}
+
+static void dhcp_send_packet(struct uip_udp_conn *conn, const char *name,
+ DhcpPacket *out, DhcpPacket *in)
+{
+ // Send the outbound packet.
+ printf("Sending %s... ", name);
+ uip_udp_packet_send(conn, out, sizeof(*out));
+ printf("done.\n");
+
+ // Prepare for the reply.
+ printf("Waiting for reply... ");
+ dhcp_in = in;
+ dhcp_out = out;
+ dhcp_in_ready = 0;
+
+ // Poll network driver until we get a reply. Resend periodically.
+ net_set_callback(&dhcp_callback);
+ for (;;) {
+ net_poll();
+ if (dhcp_in_ready)
+ break;
+ // No response, try again.
+ uip_udp_packet_send(conn, out, sizeof(*out));
+ }
+ net_set_callback(NULL);
+ printf("done.\n");
+}
+
+static void dhcp_prep_packet(DhcpPacket *packet, uint32_t transaction_id)
+{
+ memset(packet, 0, sizeof(*packet));
+ packet->opcode = DhcpRequestOp;
+ packet->hw_type = DhcpEthernet;
+ packet->hw_length = sizeof(uip_ethaddr);
+ packet->hops = 0;
+ packet->transaction_id = transaction_id;
+ packet->seconds = htonw(100);
+ assert(sizeof(uip_ethaddr) < sizeof(packet->client_hw_addr));
+ memcpy(packet->client_hw_addr, &uip_ethaddr, sizeof(uip_ethaddr));
+ memcpy(&packet->cookie, DhcpCookie, sizeof(DhcpCookie));
+}
+
+static void dhcp_add_option(uint8_t **options, uint8_t tag, uint8_t *value,
+ uint8_t length, int *remaining)
+{
+ assert(*remaining >= length + 2);
+ (*options)[0] = tag;
+ (*options)[1] = length;
+ if (length) {
+ assert(value);
+ memcpy(*options + 2, value, length);
+ }
+ *remaining -= length + 2;
+ *options += length + 2;
+}
+
+int dhcp_request(uip_ipaddr_t *next_ip, uip_ipaddr_t *server_ip,
+ const char **bootfile)
+{
+ DhcpPacket out, in;
+ uint8_t byte;
+ uint8_t *options;
+ int remaining;
+ uint8_t requested[] = { DhcpTagSubnetMask, DhcpTagDefaultRouter };
+ assert(DhcpMaxPacketSize >= DhcpMinPacketSize);
+ uint16_t max_size = htonw(DhcpMaxPacketSize);
+
+ // Set up the UDP connection.
+ uip_ipaddr_t addr;
+ uip_ipaddr(&addr, 255,255,255,255);
+ struct uip_udp_conn *conn = uip_udp_new(&addr, htonw(DhcpServerPort));
+ if (!conn) {
+ printf("Failed to set up UDP connection.\n");
+ return 1;
+ }
+ uip_udp_bind(conn, htonw(DhcpClientPort));
+
+ // Send a DHCP discover packet.
+ dhcp_prep_packet(&out, rand());
+ options = out.options;
+ remaining = sizeof(out.options);
+ byte = DhcpDiscover;
+ dhcp_add_option(&options, DhcpTagMessageType, &byte, sizeof(byte),
+ &remaining);
+ dhcp_add_option(&options, DhcpTagParameterRequestList, requested,
+ sizeof(requested), &remaining);
+ dhcp_add_option(&options, DhcpTagMaximumDhcpMessageSize,
+ (uint8_t *)&max_size, sizeof(max_size), &remaining);
+ dhcp_add_option(&options, DhcpTagEndOfList, NULL, 0, &remaining);
+ dhcp_send_packet(conn, "DHCP discover", &out, &in);
+
+ // Extract the DHCP server id.
+ uint32_t server_id;
+ if (dhcp_process_options(&in, OptionOverloadNone, &dhcp_get_server,
+ &server_id)) {
+ printf("Failed to extract server id.\n");
+ return 1;
+ }
+
+ // We got an offer. Request it.
+ dhcp_state = DhcpRequesting;
+ dhcp_prep_packet(&out, rand());
+ out.client_ip = in.your_ip;
+ options = out.options;
+ remaining = sizeof(out.options);
+ byte = DhcpRequest;
+ dhcp_add_option(&options, DhcpTagMessageType, &byte, sizeof(byte),
+ &remaining);
+ dhcp_add_option(&options, DhcpTagParameterRequestList, requested,
+ sizeof(requested), &remaining);
+ dhcp_add_option(&options, DhcpTagMaximumDhcpMessageSize,
+ (uint8_t *)&max_size, sizeof(max_size), &remaining);
+ dhcp_add_option(&options, DhcpTagServerIdentifier,
+ (uint8_t *)&server_id, sizeof(server_id), &remaining);
+ dhcp_add_option(&options, DhcpTagEndOfList, NULL, 0, &remaining);
+ dhcp_send_packet(conn, "DHCP request", &out, &in);
+
+ DhcpMessageType type;
+ if (dhcp_process_options(&in, OptionOverloadNone, &dhcp_get_type,
+ &type)) {
+ printf("Failed to extract message type.\n");
+ dhcp_state = DhcpInit;
+ return 1;
+ }
+ if (type == DhcpNak) {
+ printf("DHCP request nak-ed by the server.\n");
+ dhcp_state = DhcpInit;
+ return 1;
+ }
+
+ // The server acked, completing the transaction.
+ dhcp_state = DhcpBound;
+ uip_udp_remove(conn);
+
+ // Apply the settings.
+ if (dhcp_process_options(&in, OptionOverloadNone,
+ &dhcp_apply_options, NULL)) {
+ dhcp_state = DhcpInit;
+ return 1;
+ }
+
+ int bootfile_size = sizeof(in.bootfile_name) + 1;
+ char *file = malloc(bootfile_size);
+ if (!file) {
+ printf("Failed to allocate %d bytes for bootfile name.\n",
+ bootfile_size);
+ dhcp_state = DhcpInit;
+ return 1;
+ }
+ file[bootfile_size - 1] = 0;
+ memcpy(file, in.bootfile_name, sizeof(in.bootfile_name));
+ *bootfile = file;
+ uip_ipaddr(next_ip, in.server_ip >> 0, in.server_ip >> 8,
+ in.server_ip >> 16, in.server_ip >> 24);
+
+ uip_ipaddr(server_ip, server_id >> 0, server_id >> 8,
+ server_id >> 16, server_id >> 24);
+
+ uip_ipaddr_t my_ip;
+ uip_ipaddr(&my_ip, in.your_ip >> 0, in.your_ip >> 8,
+ in.your_ip >> 16, in.your_ip >> 24);
+ uip_sethostaddr(&my_ip);
+
+ return 0;
+}
+
+int dhcp_release(uip_ipaddr_t server_ip)
+{
+ DhcpPacket release;
+
+ // Set up the UDP connection.
+ struct uip_udp_conn *conn =
+ uip_udp_new(&server_ip, htonw(DhcpServerPort));
+ if (!conn) {
+ printf("Failed to set up UDP connection.\n");
+ return 1;
+ }
+ uip_udp_bind(conn, htonw(DhcpClientPort));
+
+ // Prepare the DHCP release packet.
+ dhcp_prep_packet(&release, rand());
+ uip_ipaddr_t my_ip;
+ uip_gethostaddr(&my_ip);
+ release.client_ip = (uip_ipaddr1(&my_ip) << 0) |
+ (uip_ipaddr2(&my_ip) << 8) |
+ (uip_ipaddr3(&my_ip) << 16) |
+ (uip_ipaddr4(&my_ip) << 24);
+ uint8_t *options = release.options;
+ int remaining = sizeof(release.options);
+ uint8_t byte = DhcpRelease;
+ dhcp_add_option(&options, DhcpTagMessageType, &byte, sizeof(byte),
+ &remaining);
+ dhcp_add_option(&options, DhcpTagEndOfList, NULL, 0, &remaining);
+
+ // Call uip_udp_packet_send directly since we won't get a reply.
+ uip_udp_packet_send(conn, &release, sizeof(release));
+
+ dhcp_state = DhcpInit;
+ uip_udp_remove(conn);
+
+ return 0;
+}
diff --git a/src/netboot/dhcp.h b/src/netboot/dhcp.h
new file mode 100644
index 0000000..dde59cd
--- /dev/null
+++ b/src/netboot/dhcp.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __NETBOOT_DHCP_H__
+#define __NETBOOT_DHCP_H__
+
+#include <stdint.h>
+
+#include "net/uip.h"
+
+int dhcp_request(uip_ipaddr_t *next_ip, uip_ipaddr_t *server_ip,
+ const char **bootfile);
+int dhcp_release(uip_ipaddr_t server_ip);
+
+#endif /* __NETBOOT_DHCP_H__ */
diff --git a/src/netboot/main.c b/src/netboot/main.c
index 4880b50..7ebc615 100644
--- a/src/netboot/main.c
+++ b/src/netboot/main.c
@@ -35,7 +35,7 @@
#include "drivers/power/power.h"
#include "net/uip.h"
#include "net/uip_arp.h"
-#include "netboot/bootp.h"
+#include "netboot/dhcp.h"
#include "netboot/tftp.h"
#include "vboot/boot.h"
#include "vboot/util/flag.h"
@@ -128,24 +128,33 @@
uip_setethaddr(*mac_addr);
// Find out who we are and what we should boot.
- uip_ipaddr_t my_ip, server_ip;
+ uip_ipaddr_t my_ip, next_ip, server_ip;
const char *bootfile;
- if (bootp(&server_ip, &bootfile)) {
- printf("Bootp failed.\n");
- halt();
- }
+ while (dhcp_request(&next_ip, &server_ip, &bootfile))
+ printf("Dhcp failed, retrying.\n");
+
printf("My ip is ");
uip_gethostaddr(&my_ip);
print_ip_addr(&my_ip);
- printf("\nThe server ip is ");
+ printf("\nThe DHCP server ip is ");
print_ip_addr(&server_ip);
+ printf("\nThe TFTP server ip is ");
+ print_ip_addr(&next_ip);
printf("\nThe boot file is %s\n", bootfile);
uint32_t size;
- if (tftp_read(payload, &server_ip, bootfile, &size)) {
+ if (tftp_read(payload, &next_ip, bootfile, &size)) {
printf("Tftp failed.\n");
+ if (dhcp_release(server_ip))
+ printf("Dhcp release failed.\n");
halt();
}
+
+ if (dhcp_release(server_ip)) {
+ printf("Dhcp release failed.\n");
+ halt();
+ }
+
printf("The bootfile was %d bytes long.\n", size);
char *cmd_line = CONFIG_NETBOOT_DEFAULT_COMMAND_LINE;