X-Git-Url: http://git.ozlabs.org/?p=yaboot.git;a=blobdiff_plain;f=second%2Ffile.c;h=02b187231716e318964dc77681637d000ad2a35a;hp=e5135995a74e33fb73ebec5e318b8baa71c5f033;hb=caa5b9fc39eab729963d70d9910b6319a866fae4;hpb=98eacaa1983cde1532e80e67466dfa615e5564a2 diff --git a/second/file.c b/second/file.c index e513599..02b1872 100644 --- a/second/file.c +++ b/second/file.c @@ -38,8 +38,57 @@ 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 + * Copy the string from source to dest until the end of string or comma is seen * in the source. * Move source and dest pointers respectively. * Returns pointer to the start of the string that has just been copied. @@ -55,26 +104,21 @@ scopy(char **dest, char **source) while (**source != ',' && **source != '\0') *(*dest)++ = *(*source)++; if (**source != '\0') - *(*source)++; + (void)*(*source)++; **dest = '\0'; - *(*dest)++; + (void)*(*dest)++; return ret; } /* - * 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; @@ -119,21 +163,224 @@ extract_args_from_netdev_path(char *imagepath, struct boot_fspec_t *result) args++; /* If comma(,) is not immediately followed by ':' then go past the , */ /* - * read the arguments in order: siaddr,filename,ciaddr,giaddr, + * read the arguments in order: vtag,siaddr,filename,ciaddr,giaddr, * bootp-retries,tftp-retries,addl_prameters */ - result->siaddr = scopy(&str, &args); + if ((tmp = strstr(imagepath, "vtag=")) != NULL) { + result->vtag = scopy(&str, &tmp); + args = tmp; + } + + 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; 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. + */ +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, "vtag=")) != NULL) + result->vtag = 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); @@ -141,7 +388,10 @@ 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 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) @@ -280,10 +530,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); @@ -430,6 +683,9 @@ int open_file(struct boot_fspec_t* spec, struct boot_file_t* file) 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);