extern char bootdevice[];
+/* Convert __u32 into std, dotted quad string, leaks like a sive :( */
+static char *
+ipv4_to_str(__u32 ip)
+{
+ char *buf = malloc(sizeof("000.000.000.000"));
+
+ sprintf(buf,"%u.%u.%u.%u",
+ (ip & 0xff000000) >> 24, (ip & 0x00ff0000) >> 16,
+ (ip & 0x0000ff00) >> 8, (ip & 0x000000ff));
+
+ return buf;
+}
+
+/* Ensure the string arg is a plausible IPv4 address */
+static char * is_valid_ipv4_str(char *str)
+{
+ int i;
+ long tmp;
+ __u32 ip = 0;
+ char *ptr=str, *endptr;
+
+ if (str == NULL)
+ return NULL;
+
+ for (i=0; i<4; i++, ptr = ++endptr) {
+ tmp = strtol(ptr, &endptr, 10);
+ if ((tmp & 0xff) != tmp)
+ return NULL;
+
+ /* If we reach the end of the string but we're not in the 4th octet
+ * we have an invalid IP */
+ if (*endptr == '\x0' && i!=3)
+ return NULL;
+
+ /* If we have anything other than a NULL or '.' we have an invlaid
+ * IP */
+ if (*endptr != '\x0' && *endptr != '.')
+ return NULL;
+
+ ip += (tmp << (24-(i*8)));
+ }
+
+ if (ip == 0 || ip == ~0u)
+ return NULL;
+
+ return str;
+}
+
+
/*
* Copy the string from source to dest till newline or comma(,) is seen
* in the source.
}
/*
- * Extract all the arguments provided in the imagepath and fill it in result.
+ * Extract all the ipv4 arguments from the bootpath provided and fill result
* Returns 1 on success, 0 on failure.
*/
static int
-extract_args_from_netdev_path(char *imagepath, struct boot_fspec_t *result)
+extract_ipv4_args(char *imagepath, struct boot_fspec_t *result)
{
char *tmp, *args, *str, *start;
- DEBUG_F("imagepath = %s\n", imagepath);
-
- if (!imagepath)
- return 1;
-
args = strrchr(imagepath, ':');
if (!args)
return 1;
* read the arguments in order: siaddr,filename,ciaddr,giaddr,
* bootp-retries,tftp-retries,addl_prameters
*/
- result->siaddr = scopy(&str, &args);
+ result->siaddr = is_valid_ipv4_str(scopy(&str, &args));
result->file = scopy(&str, &args);
- result->ciaddr = scopy(&str, &args);
- result->giaddr = scopy(&str, &args);
+ result->ciaddr = is_valid_ipv4_str(scopy(&str, &args));
+ result->giaddr = is_valid_ipv4_str(scopy(&str, &args));
result->bootp_retries = scopy(&str, &args);
result->tftp_retries = scopy(&str, &args);
+ result->subnetmask = is_valid_ipv4_str(scopy(&str, &args));
if (*args) {
result->addl_params = strdup(args);
if (!result->addl_params)
return 0;
}
+ return 1;
+}
+
+/* DHCP options */
+enum dhcp_options {
+ DHCP_PAD = 0,
+ DHCP_NETMASK = 1,
+ DHCP_ROUTERS = 3,
+ 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 = 0;
+
+ if (tag == DHCP_PAD)
+ continue;
+
+ len = options[i++];
+ /* Clamp the maxium length of the memcpy() to the right size for
+ * value. */
+ if (len > sizeof(value))
+ memcpy(&value, &options[i], sizeof(value));
+ else
+ 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:
+ if ((result->subnetmask == NULL ||
+ *(result->subnetmask) == '\x0') && value != 0) {
+ result->subnetmask = ipv4_to_str(value);
+ DEBUG_F("Storing %s as subnetmask from options\n",
+ result->subnetmask);
+ }
+ 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.
+ */
+static void
+extract_netinfo_args(struct boot_fspec_t *result)
+{
+ struct bootp_packet *packet;
+
+ /* Check to see if we can get the [scyg]iaddr fields from netinfo */
+ packet = prom_get_netinfo();
+ if (!packet)
+ return;
+
+ DEBUG_F("We have a boot packet\n");
+ DEBUG_F(" siaddr = <%x>\n", packet->siaddr);
+ DEBUG_F(" ciaddr = <%x>\n", packet->ciaddr);
+ DEBUG_F(" yiaddr = <%x>\n", packet->yiaddr);
+ DEBUG_F(" giaddr = <%x>\n", packet->giaddr);
+
+ /* Try to fallback to yiaddr if ciaddr is empty. Broken? */
+ if (packet->ciaddr == 0 && packet->yiaddr != 0)
+ packet->ciaddr = packet->yiaddr;
+
+ if ((result->siaddr == NULL || *(result->siaddr) == '\x0')
+ && packet->siaddr != 0)
+ result->siaddr = ipv4_to_str(packet->siaddr);
+ if ((result->ciaddr == NULL || *(result->ciaddr) == '\x0')
+ && packet->ciaddr != 0)
+ result->ciaddr = ipv4_to_str(packet->ciaddr);
+ 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
+ * redirections */
+ if (result->giaddr == NULL) {
+ result->giaddr = ipv4_to_str(packet->siaddr);
+ DEBUG_F("Forcing giaddr to siaddr <%s>\n", result->giaddr);
+ }
+}
+
+/*
+ * Extract all the ipv6 arguments from the bootpath provided and fill result
+ * Syntax: ipv6,[dhcpv6[=diaddr,]]ciaddr=c_iaddr,giaddr=g_iaddr,siaddr=s_iaddr,
+ * filename=file_name,tftp-retries=tftp_retries,blksize=block_size
+ * Returns 1 on success, 0 on failure.
+ */
+static int
+extract_ipv6_args(char *imagepath, struct boot_fspec_t *result)
+{
+ char *str, *tmp;
+ int total_len;
+
+ result->is_ipv6 = 1;
+
+ /* Just allocate the max required size */
+ total_len = strlen(imagepath) + 1;
+ str = malloc(total_len);
+ if (!str)
+ return 0;
+
+ if ((tmp = strstr(imagepath, "dhcpv6=")) != NULL)
+ result->dhcpv6 = scopy(&str, &tmp);
+
+ if ((tmp = strstr(imagepath, "ciaddr=")) != NULL)
+ result->ciaddr = scopy(&str, &tmp);
+
+ if ((tmp = strstr(imagepath, "giaddr=")) != NULL)
+ result->giaddr = scopy(&str, &tmp);
+
+ if ((tmp = strstr(imagepath, "siaddr=")) != NULL)
+ result->siaddr = scopy(&str, &tmp);
+
+ if ((tmp = strstr(imagepath, "filename=")) != NULL)
+ result->file = scopy(&str, &tmp);
+
+ if ((tmp = strstr(imagepath, "tftp-retries=")) != NULL)
+ result->tftp_retries = scopy(&str, &tmp);
+
+ if ((tmp = strstr(imagepath, "blksize=")) != NULL)
+ result->blksize = scopy(&str, &tmp);
+ return 1;
+}
+
+/*
+ * Extract all the arguments provided in the imagepath and fill it in result.
+ * Returns 1 on success, 0 on failure.
+ */
+static int
+extract_netboot_args(char *imagepath, struct boot_fspec_t *result)
+{
+ int ret;
+
+ DEBUG_F("imagepath = %s\n", imagepath);
+
+ if (!imagepath)
+ return 1;
+
+ if (strstr(imagepath, TOK_IPV6))
+ ret = extract_ipv6_args(imagepath, result);
+ else
+ ret = extract_ipv4_args(imagepath, result);
+ extract_netinfo_args(result);
+
+ DEBUG_F("ipv6 = <%d>\n", result->is_ipv6);
DEBUG_F("siaddr = <%s>\n", result->siaddr);
DEBUG_F("file = <%s>\n", result->file);
DEBUG_F("ciaddr = <%s>\n", result->ciaddr);
DEBUG_F("bootp_retries = <%s>\n", result->bootp_retries);
DEBUG_F("tftp_retries = <%s>\n", result->tftp_retries);
DEBUG_F("addl_params = <%s>\n", result->addl_params);
- return 1;
+ DEBUG_F("dhcpv6 = <%s>\n", result->dhcpv6);
+ DEBUG_F("blksize = <%s>\n", result->blksize);
+
+ return ret;
}
static char *netdev_path_to_dev(const char *path)
if (device_kind == FILE_DEVICE_NET) {
if (strchr(ipath, ':')) {
- if (extract_args_from_netdev_path(ipath, result) == 0)
+ if (extract_netboot_args(ipath, result) == 0)
return 0;
- } else
+ } else {
+ /* If we didn't get a ':' then look only in netinfo */
+ extract_netinfo_args(result);
result->file = strdup(ipath);
+ }
if (!defdev)
result->dev = netdev_path_to_dev(ipath);
case FILE_DEVICE_BLOCK:
DEBUG_F("device is a block device\n");
return file_block_open(file, spec, spec->part);
+ case FILE_DEVICE_ISCSI:
+ DEBUG_F("device is a iSCSI device\n");
+ return file_block_open(file, spec, spec->part);
case FILE_DEVICE_NET:
DEBUG_F("device is a network device\n");
return file_net_open(file, spec);