X-Git-Url: http://git.ozlabs.org/?a=blobdiff_plain;ds=sidebyside;f=second%2Ffile.c;h=0ad981bbf99c4aaaf2ae8c3ddf076e5dbfb95e8f;hb=f91756b6306e4da8aef013c8b375b32c2c0c3a2f;hp=875cad807063efaac91c729648786a470d4e938c;hpb=3638d19d001798b1b3dabd486501dd00b6f5b492;p=yaboot.git diff --git a/second/file.c b/second/file.c index 875cad8..0ad981b 100644 --- a/second/file.c +++ b/second/file.c @@ -38,6 +38,19 @@ 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; +} + /* * Copy the string from source to dest till newline or comma(,) is seen * in the source. @@ -131,12 +144,134 @@ 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; jgiaddr == 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. + * + * Returns 1 on success, 0 on failure. + */ +static int +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 0; + + 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); + } + + 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_args_from_netdev_path(char *imagepath, struct boot_fspec_t *result) +extract_netboot_args(char *imagepath, struct boot_fspec_t *result) { int ret; @@ -146,6 +281,7 @@ extract_args_from_netdev_path(char *imagepath, struct boot_fspec_t *result) return 1; ret = extract_ipv4_args(imagepath, result); + ret |= extract_netinfo_args(result); DEBUG_F("siaddr = <%s>\n", result->siaddr); DEBUG_F("file = <%s>\n", result->file); @@ -154,6 +290,7 @@ extract_args_from_netdev_path(char *imagepath, struct boot_fspec_t *result) 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 ret; } @@ -293,10 +430,13 @@ parse_device_path(char *imagepath, char *defdevice, int defpart, 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);