X-Git-Url: http://git.ozlabs.org/?p=yaboot.git;a=blobdiff_plain;f=second%2Ffile.c;h=debf7f42b87e8d87244c5623fd736038be24f2bf;hp=5d5ccbdf10bd2df10b687fb2d5ae867025a74772;hb=3901c5b44c63c5bf9f576d0dd7646241b8ee4088;hpb=8e64db245a2d402dc89fa1f68a3789203815b238 diff --git a/second/file.c b/second/file.c index 5d5ccbd..debf7f4 100644 --- a/second/file.c +++ b/second/file.c @@ -51,6 +51,42 @@ ipv4_to_str(__u32 ip) 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. @@ -130,12 +166,13 @@ extract_ipv4_args(char *imagepath, struct boot_fspec_t *result) * 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) @@ -144,13 +181,88 @@ 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; jsubnetmask == 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. - * - * Returns 1 on success, 0 on failure. */ -static int +static void extract_netinfo_args(struct boot_fspec_t *result) { struct bootp_packet *packet; @@ -158,7 +270,7 @@ extract_netinfo_args(struct boot_fspec_t *result) /* Check to see if we can get the [scyg]iaddr fields from netinfo */ packet = prom_get_netinfo(); if (!packet) - return 0; + return; DEBUG_F("We have a boot packet\n"); DEBUG_F(" siaddr = <%x>\n", packet->siaddr); @@ -170,16 +282,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 @@ -188,6 +302,48 @@ extract_netinfo_args(struct boot_fspec_t *result) 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; } @@ -206,9 +362,13 @@ extract_netboot_args(char *imagepath, struct boot_fspec_t *result) if (!imagepath) return 1; - ret = extract_ipv4_args(imagepath, result); - ret |= extract_netinfo_args(result); + 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); @@ -216,7 +376,9 @@ extract_netboot_args(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); - + DEBUG_F("dhcpv6 = <%s>\n", result->dhcpv6); + DEBUG_F("blksize = <%s>\n", result->blksize); + return ret; }