Add parseing for Vendor options aka DHCP options.
authorTony Breeds <tony@bakeyournoodle.com>
Tue, 5 May 2009 06:14:20 +0000 (16:14 +1000)
committerTony Breeds <tony@bakeyournoodle.com>
Thu, 22 Oct 2009 05:09:14 +0000 (16:09 +1100)
If we have DHCP options process them.

Signed-off-by: Tony Breeds <tony@bakeyournoodle.com>
include/prom.h
second/file.c
second/prom.c

index eacee7708c1f9300e87b7cca8db84f01aad0d00a..e0397ecaea2276b28be552f8ad462cab2db95da3 100644 (file)
@@ -152,7 +152,7 @@ struct bootp_packet {
      unsigned char chaddr[16];
      unsigned char sname[64];
      unsigned char file[128];
-     /* vendor options go here if we need them */
+     unsigned char options[]; /* vendor options */
 };
 
 struct bootp_packet * prom_get_netinfo (void);
index 5d5ccbdf10bd2df10b687fb2d5ae867025a74772..0ad981bbf99c4aaaf2ae8c3ddf076e5dbfb95e8f 100644 (file)
@@ -144,6 +144,78 @@ extract_ipv4_args(char *imagepath, struct boot_fspec_t *result)
      return 1;
 }
 
+/* DHCP options */
+enum dhcp_options {
+     DHCP_PAD = 0,
+     DHCP_NETMASK = 1,
+     DHCP_ROUTERS = 3,
+     DHCP_DNS = 6,
+     DHCP_END = 255,
+};
+
+#define DHCP_COOKIE        0x63825363
+#define DHCP_COOKIE_SIZE   4
+
+/*
+ * Process the bootp reply packet's vendor extensions.
+ * Vendor extensions are detailed in: http://www.faqs.org/rfcs/rfc1084.html
+ */
+static void
+extract_vendor_options(struct bootp_packet *packet, struct boot_fspec_t *result)
+{
+     int i = 0;
+     __u32 cookie;
+     __u8 *options = &packet->options[0];
+
+     memcpy(&cookie, &options[i], DHCP_COOKIE_SIZE);
+
+     if (cookie != DHCP_COOKIE) {
+          prom_printf("EEEK! cookie is fubar got %08x expected %08x\n",
+                      cookie, DHCP_COOKIE);
+          return;
+     }
+
+     i += DHCP_COOKIE_SIZE;
+
+     /* FIXME: It may be possible to run off the end of a packet here /if/
+      *         it's malformed. :( */
+     while (options[i] != DHCP_END) {
+          __u8 tag = options[i++], len;
+          __u32 value;
+
+          if (tag == DHCP_PAD)
+               continue;
+
+          len = options[i++];
+          memcpy(&value, &options[i], len);
+
+#if DEBUG
+{
+     DEBUG_F("tag=%2d, len=%2d, data=", tag, len);
+     int j;
+     for (j=0; j<len; j++)
+          prom_printf("%02x", options[i+j]);
+     prom_printf("\n");
+}
+#endif
+
+          switch (tag) {
+               case DHCP_NETMASK:
+                    /* FIXME: do we need to grok the subnet mask? */
+                    break;
+               case DHCP_ROUTERS:
+                    if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
+                        && value != 0) {
+                         result->giaddr = ipv4_to_str(value);
+                         DEBUG_F("Storing %s as gateway from options\n",
+                                 result->giaddr);
+                    }
+                    break;
+               }
+          i += len;
+     }
+}
+
 /*
  * Check netinfo for ipv4 parameters and add them to the fspec iff the
  * fspec has no existing value.
@@ -170,16 +242,18 @@ extract_netinfo_args(struct boot_fspec_t *result)
      if (packet->ciaddr == 0 && packet->yiaddr != 0)
           packet->ciaddr = packet->yiaddr;
 
-     if ((result->siaddr == NULL || *(result->siaddr) == NULL)
+     if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
          && packet->siaddr != 0)
           result->siaddr = ipv4_to_str(packet->siaddr);
-     if ((result->ciaddr == NULL || *(result->ciaddr) == NULL)
+     if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
          && packet->ciaddr != 0)
           result->ciaddr = ipv4_to_str(packet->ciaddr);
-     if ((result->giaddr == NULL || *(result->giaddr) == NULL)
+     if ((result->giaddr == NULL || *(result->giaddr) == '\x0')
          && packet->giaddr != 0)
           result->giaddr = ipv4_to_str(packet->giaddr);
 
+     extract_vendor_options(packet, result);
+
      /* FIXME: Yck! if we /still/ do not have a gateway then "cheat" and use
       *        the server.  This will be okay if the client and server are on
       *        the same IP network, if not then lets hope the server does ICMP
index 4ad727761b47a58f2a6b47869df912dd3de04df4..d48ede52b071025e12a5514f8e604d89c5e9e400 100644 (file)
@@ -683,6 +683,10 @@ struct bootp_packet * prom_get_netinfo (void)
      void *bootp_response = NULL;
      char *propname;
      struct bootp_packet *packet;
+     /* struct bootp_packet contains a VLA, so sizeof won't work.
+        the VLA /must/ be the last field in the structure so use it's
+        offset as a good estimate of the packet size */
+     size_t packet_size = offsetof(struct bootp_packet, options);
      int i = 0, size, offset = 0;
      prom_handle chosen;
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
@@ -707,7 +711,7 @@ struct bootp_packet * prom_get_netinfo (void)
      if (size <= 0)
          return NULL;
 
-     if (sizeof(*packet) > size - offset) {
+     if (packet_size > size - offset) {
          prom_printf("Malformed %s property?\n", propname);
          return NULL;
      }