diff options
Diffstat (limited to 'roms/SLOF/lib/libnet/netload.c')
-rw-r--r-- | roms/SLOF/lib/libnet/netload.c | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/roms/SLOF/lib/libnet/netload.c b/roms/SLOF/lib/libnet/netload.c new file mode 100644 index 000000000..ae169f3e0 --- /dev/null +++ b/roms/SLOF/lib/libnet/netload.c @@ -0,0 +1,804 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <unistd.h> +#include <tftp.h> +#include <ethernet.h> +#include <dhcp.h> +#include <dhcpv6.h> +#include <ipv4.h> +#include <ipv6.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <libbootmsg/libbootmsg.h> +#include <helpers.h> +#include "args.h" +#include "netapps.h" +#include "pxelinux.h" + +#define IP_INIT_DEFAULT 5 +#define IP_INIT_NONE 0 +#define IP_INIT_BOOTP 1 +#define IP_INIT_DHCP 2 +#define IP_INIT_DHCPV6_STATELESS 3 +#define IP_INIT_IPV6_MANUAL 4 + +#define MAX_PKT_SIZE 1720 +#define DEFAULT_BOOT_RETRIES 10 +#define DEFAULT_TFTP_RETRIES 20 +static int ip_version; + +typedef struct { + char filename[100]; + int ip_init; + char siaddr[4]; + ip6_addr_t si6addr; + char ciaddr[4]; + ip6_addr_t ci6addr; + char giaddr[4]; + ip6_addr_t gi6addr; + int bootp_retries; + int tftp_retries; +} obp_tftp_args_t; + +/** + * Print error with preceeding error code + */ +static void netload_error(int errcode, const char *format, ...) +{ + va_list vargs; + char buf[256]; + int elen; + + elen = sprintf(buf, "E%04X: (net) ", errcode); + + va_start(vargs, format); + vsnprintf(&buf[elen], sizeof(buf) - elen, format, vargs); + va_end(vargs); + + bootmsg_error(errcode, &buf[elen - 6]); + write_mm_log(buf, strlen(buf), 0x91); +} + +/** + * Parses a argument string for IPv6 booting, extracts all + * parameters and fills a structure accordingly + * + * @param arg_str string with arguments, separated with ',' + * @param argc number of arguments + * @param obp_tftp_args structure which contains the result + * @return updated arg_str + */ +static const char * +parse_ipv6args (const char *arg_str, unsigned int argc, + obp_tftp_args_t *obp_tftp_args) +{ + char *ptr = NULL; + char arg_buf[100]; + + // find out siaddr + if (argc == 0) + memset(&obp_tftp_args->si6addr.addr, 0, 16); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->si6addr.addr[0]))) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(&obp_tftp_args->si6addr.addr, 0, 16); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(&obp_tftp_args->si6addr.addr, 0, 16); + } + + // find out filename + if (argc == 0) + obp_tftp_args->filename[0] = 0; + else { + argncpy(arg_str, 0, obp_tftp_args->filename, 100); + for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr) + if(*ptr == '\\') { + *ptr = '/'; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out ciaddr + if (argc == 0) + memset(&obp_tftp_args->ci6addr, 0, 16); + else { + argncpy(arg_str, 0, arg_buf, 100); + if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->ci6addr.addr[0]))) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(&obp_tftp_args->ci6addr.addr, 0, 16); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(&obp_tftp_args->ci6addr.addr, 0, 16); + } + + // find out giaddr + if (argc == 0) + memset(&obp_tftp_args->gi6addr, 0, 16); + else { + argncpy(arg_str, 0, arg_buf, 100); + if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->gi6addr.addr)) ) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(&obp_tftp_args->gi6addr, 0, 16); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(&obp_tftp_args->gi6addr.addr, 0, 16); + } + + return arg_str; +} + + +/** + * Parses a argument string for IPv4 booting, extracts all + * parameters and fills a structure accordingly + * + * @param arg_str string with arguments, separated with ',' + * @param argc number of arguments + * @param obp_tftp_args structure which contains the result + * @return updated arg_str + */ +static const char * +parse_ipv4args (const char *arg_str, unsigned int argc, + obp_tftp_args_t *obp_tftp_args) +{ + char *ptr = NULL; + char arg_buf[100]; + + // find out siaddr + if(argc==0) { + memset(obp_tftp_args->siaddr, 0, 4); + } else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->siaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->siaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->siaddr, 0, 4); + } + + // find out filename + if(argc==0) + obp_tftp_args->filename[0] = 0; + else { + argncpy(arg_str, 0, obp_tftp_args->filename, 100); + for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr) + if(*ptr == '\\') + *ptr = '/'; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out ciaddr + if(argc==0) + memset(obp_tftp_args->ciaddr, 0, 4); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->ciaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->ciaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->ciaddr, 0, 4); + } + + // find out giaddr + if(argc==0) + memset(obp_tftp_args->giaddr, 0, 4); + else { + argncpy(arg_str, 0, arg_buf, 100); + if(strtoip(arg_buf, obp_tftp_args->giaddr)) { + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(arg_buf[0] == 0) { + memset(obp_tftp_args->giaddr, 0, 4); + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else + memset(obp_tftp_args->giaddr, 0, 4); + } + + return arg_str; +} + +/** + * Parses a argument string which is given by netload, extracts all + * parameters and fills a structure according to this + * + * Netload-Parameters: + * [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries + * + * @param arg_str string with arguments, separated with ',' + * @param obp_tftp_args structure which contains the result + * @return none + */ +static void +parse_args(const char *arg_str, obp_tftp_args_t *obp_tftp_args) +{ + unsigned int argc; + char arg_buf[100]; + + memset(obp_tftp_args, 0, sizeof(*obp_tftp_args)); + + argc = get_args_count(arg_str); + + // find out if we should use BOOTP or DHCP + if(argc==0) + obp_tftp_args->ip_init = IP_INIT_DEFAULT; + else { + argncpy(arg_str, 0, arg_buf, 100); + if (strcasecmp(arg_buf, "bootp") == 0) { + obp_tftp_args->ip_init = IP_INIT_BOOTP; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(strcasecmp(arg_buf, "dhcp") == 0) { + obp_tftp_args->ip_init = IP_INIT_DHCP; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + else if(strcasecmp(arg_buf, "ipv6") == 0) { + obp_tftp_args->ip_init = IP_INIT_DHCPV6_STATELESS; + arg_str = get_arg_ptr(arg_str, 1); + --argc; + ip_version = 6; + } + else + obp_tftp_args->ip_init = IP_INIT_DEFAULT; + } + + if (ip_version == 4) { + arg_str = parse_ipv4args (arg_str, argc, obp_tftp_args); + } + else if (ip_version == 6) { + arg_str = parse_ipv6args (arg_str, argc, obp_tftp_args); + } + + // find out bootp-retries + if (argc == 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + else { + argncpy(arg_str, 0, arg_buf, 100); + if(arg_buf[0] == 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + else { + obp_tftp_args->bootp_retries = strtol(arg_buf, 0, 10); + if(obp_tftp_args->bootp_retries < 0) + obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } + + // find out tftp-retries + if (argc == 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + else { + argncpy(arg_str, 0, arg_buf, 100); + if(arg_buf[0] == 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + else { + obp_tftp_args->tftp_retries = strtol(arg_buf, 0, 10); + if(obp_tftp_args->tftp_retries < 0) + obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES; + } + arg_str = get_arg_ptr(arg_str, 1); + --argc; + } +} + +/** + * DHCP: Wrapper for obtaining IP and configuration info from DHCP server + * for both IPv4 and IPv6. + * (makes several attempts). + * + * @param ret_buffer buffer for returning BOOTP-REPLY packet data + * @param fn_ip contains the following configuration information: + * client MAC, client IP, TFTP-server MAC, + * TFTP-server IP, Boot file name + * @param retries No. of DHCP attempts + * @param flags flags for specifying type of dhcp attempt (IPv4/IPv6) + * ZERO - attempt DHCPv4 followed by DHCPv6 + * F_IPV4 - attempt only DHCPv4 + * F_IPV6 - attempt only DHCPv6 + * @return ZERO - IP and configuration info obtained; + * NON ZERO - error condition occurs. + */ +int dhcp(char *ret_buffer, struct filename_ip *fn_ip, unsigned int retries, + int flags) +{ + int i = (int) retries+1; + int rc = -1; + + printf(" Requesting information via DHCP%s: ", + flags == F_IPV4 ? "v4" : flags == F_IPV6 ? "v6" : ""); + + if (flags != F_IPV6) + dhcpv4_generate_transaction_id(); + if (flags != F_IPV4) + dhcpv6_generate_transaction_id(); + + do { + printf("\b\b\b%03d", i-1); + if (getchar() == 27) { + printf("\nAborted\n"); + return -1; + } + if (!--i) { + printf("\nGiving up after %d DHCP requests\n", retries); + return -1; + } + if (!flags || (flags == F_IPV4)) { + ip_version = 4; + rc = dhcpv4(ret_buffer, fn_ip); + } + if ((!flags && (rc == -1)) || (flags == F_IPV6)) { + ip_version = 6; + set_ipv6_address(fn_ip->fd, 0); + rc = dhcpv6(ret_buffer, fn_ip); + if (rc == 0) { + memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16); + break; + } + + } + if (rc != -1) /* either success or non-dhcp failure */ + break; + } while (1); + printf("\b\b\b\bdone\n"); + + return rc; +} + +/** + * Seed the random number generator with our mac and current timestamp + */ +static void seed_rng(uint8_t mac[]) +{ + unsigned int seed; + + asm volatile("mftbl %0" : "=r"(seed)); + seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5]; + srand(seed); +} + +static int tftp_load(filename_ip_t *fnip, void *buffer, int len, + unsigned int retries) +{ + tftp_err_t tftp_err; + int rc; + + rc = tftp(fnip, buffer, len, retries, &tftp_err); + + if (rc > 0) { + printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, + rc / 1024); + } else { + int ecode; + const char *errstr = NULL; + rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode); + if (errstr) + netload_error(ecode, errstr); + } + + return rc; +} + +static const char *get_uuid(void) +{ + char *addr; + int len; + + if (SLOF_get_property("/", "system-id", &addr, &len)) + return NULL; + if (len < 37) { /* This should never happen... */ + puts("Warning: UUID property is too short."); + return NULL; + } + + return addr; +} + +#define CFG_BUF_SIZE 2048 +#define MAX_PL_CFG_ENTRIES 16 +static int net_pxelinux_load(filename_ip_t *fnip, char *loadbase, + int maxloadlen, uint8_t *mac, int retries) +{ + struct pl_cfg_entry entries[MAX_PL_CFG_ENTRIES]; + int def, rc, ilen; + static char *cfgbuf; + + cfgbuf = malloc(CFG_BUF_SIZE); + if (!cfgbuf) { + puts("Not enough memory for pxelinux config file buffer!"); + return -1; + } + + rc = pxelinux_load_parse_cfg(fnip, mac, get_uuid(), retries, + cfgbuf, CFG_BUF_SIZE, + entries, MAX_PL_CFG_ENTRIES, &def); + if (rc < 0) + goto out_free; + if (rc == 0) { + puts("No valid entries in pxelinux config file."); + rc = -1; + goto out_free; + } + + /* Load kernel */ + strncpy(fnip->filename, entries[def].kernel, + sizeof(fnip->filename) - 1); + fnip->filename[sizeof(fnip->filename) - 1] = 0; + rc = tftp_load(fnip, loadbase, maxloadlen, retries); + if (rc <= 0) + goto out_free; + + /* Load ramdisk */ + if (entries[def].initrd) { + loadbase += rc; + maxloadlen -= rc; + if (maxloadlen <= 0) { + puts(" Not enough space for loading the initrd!"); + rc = -1; + goto out_free; + } + strncpy(fnip->filename, entries[def].initrd, + sizeof(fnip->filename) - 1); + ilen = tftp_load(fnip, loadbase, maxloadlen, retries); + if (ilen < 0) { + rc = ilen; + goto out_free; + } + /* The ELF loader will move the kernel to some spot in low mem + * later, thus move the initrd to the end of the RAM instead */ + memmove(loadbase + maxloadlen - ilen, loadbase, ilen); + /* Encode the initrd information in the device tree */ + SLOF_set_chosen_int("linux,initrd-start", + (long)loadbase + maxloadlen - ilen); + SLOF_set_chosen_int("linux,initrd-end", + (long)loadbase + maxloadlen); + } + + if (entries[def].append) { + SLOF_set_chosen_bytes("bootargs", entries[def].append, + strlen(entries[def].append) + 1); + } + +out_free: + free(cfgbuf); + return rc; +} + +static void encode_response(char *pkt_buffer, size_t size, int ip_init) +{ + switch(ip_init) { + case IP_INIT_BOOTP: + SLOF_encode_bootp_response(pkt_buffer, size); + break; + case IP_INIT_DHCP: + case IP_INIT_DHCPV6_STATELESS: + case IP_INIT_DEFAULT: + SLOF_encode_dhcp_response(pkt_buffer, size); + break; + default: + break; + } +} + +int netload(char *buffer, int len, char *args_fs, unsigned alen) +{ + int rc, filename_len; + filename_ip_t fn_ip; + int fd_device; + obp_tftp_args_t obp_tftp_args; + char null_ip[4] = { 0x00, 0x00, 0x00, 0x00 }; + char null_ip6[16] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + uint8_t own_mac[6]; + char *pkt_buffer; + + ip_version = 4; + + pkt_buffer = SLOF_alloc_mem(MAX_PKT_SIZE); + if (!pkt_buffer) { + puts("ERROR: Unable to allocate memory"); + return -1; + } + memset(pkt_buffer, 0, MAX_PKT_SIZE); + + puts("\n Initializing NIC"); + memset(&fn_ip, 0, sizeof(filename_ip_t)); + + /*********************************************************** + * + * Initialize network stuff and retrieve boot informations + * + ***********************************************************/ + + /* Wait for link up and get mac_addr from device */ + for(rc=0; rc<DEFAULT_BOOT_RETRIES; ++rc) { + if(rc > 0) { + set_timer(TICKS_SEC); + while (get_timer() > 0); + } + fd_device = socket(AF_INET, SOCK_DGRAM, 0, (char*) own_mac); + if(fd_device != -2) + break; + if(getchar() == 27) { + fd_device = -2; + break; + } + } + + if (fd_device == -1) { + netload_error(0x3000, "Could not read MAC address"); + rc = -100; + goto err_out; + } + else if (fd_device == -2) { + netload_error(0x3006, "Could not initialize network device"); + rc = -101; + goto err_out; + } + + fn_ip.fd = fd_device; + + printf(" Reading MAC address from device: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + own_mac[0], own_mac[1], own_mac[2], + own_mac[3], own_mac[4], own_mac[5]); + + // init ethernet layer + set_mac_address(own_mac); + + seed_rng(own_mac); + + if (alen > 0) { + char args[256]; + if (alen > sizeof(args) - 1) { + puts("ERROR: Parameter string is too long."); + rc = -7; + goto err_out; + } + /* Convert forth string into NUL-terminated C-string */ + strncpy(args, args_fs, alen); + args[alen] = 0; + parse_args(args, &obp_tftp_args); + if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES) + obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES; + else + obp_tftp_args.bootp_retries -= rc; + } + else { + memset(&obp_tftp_args, 0, sizeof(obp_tftp_args_t)); + obp_tftp_args.ip_init = IP_INIT_DEFAULT; + obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES; + obp_tftp_args.tftp_retries = DEFAULT_TFTP_RETRIES; + } + memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4); + + // reset of error code + rc = 0; + + /* if we still have got all necessary parameters, then we don't + need to perform an BOOTP/DHCP-Request */ + if (ip_version == 4) { + if (memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0 + && obp_tftp_args.filename[0] != 0) { + + memcpy(&fn_ip.server_ip, &obp_tftp_args.siaddr, 4); + obp_tftp_args.ip_init = IP_INIT_NONE; + } + } + else if (ip_version == 6) { + if (memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0 + && obp_tftp_args.filename[0] != 0) { + memcpy(&fn_ip.server_ip6.addr[0], + &obp_tftp_args.si6addr.addr, 16); + obp_tftp_args.ip_init = IP_INIT_IPV6_MANUAL; + } + else { + obp_tftp_args.ip_init = IP_INIT_DHCPV6_STATELESS; + } + } + + // construction of fn_ip from parameter + switch(obp_tftp_args.ip_init) { + case IP_INIT_BOOTP: + // if giaddr in not specified, then we have to identify + // the BOOTP server via broadcasts + if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) { + // don't do this, when using DHCP !!! + fn_ip.server_ip = 0xFFFFFFFF; + } + // if giaddr is specified, then we have to use this + // IP address as proxy to identify the BOOTP server + else { + memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4); + } + rc = bootp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries); + break; + case IP_INIT_DHCP: + rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4); + break; + case IP_INIT_DHCPV6_STATELESS: + rc = dhcp(pkt_buffer, &fn_ip, + obp_tftp_args.bootp_retries, F_IPV6); + break; + case IP_INIT_IPV6_MANUAL: + if (memcmp(&obp_tftp_args.ci6addr, null_ip6, 16)) { + set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr); + } else { + /* + * If no client address has been specified, then + * use a link-local or stateless autoconfig address + */ + set_ipv6_address(fn_ip.fd, NULL); + memcpy(&fn_ip.own_ip6, get_ipv6_address(), 16); + } + break; + case IP_INIT_DEFAULT: + rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0); + break; + case IP_INIT_NONE: + default: + break; + } + + if(rc >= 0 && ip_version == 4) { + if(memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.ciaddr, &fn_ip.own_ip, 4) != 0) + memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4); + + if(memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0 + && memcmp(obp_tftp_args.siaddr, &fn_ip.server_ip, 4) != 0) + memcpy(&fn_ip.server_ip, obp_tftp_args.siaddr, 4); + + // init IPv4 layer + set_ipv4_address(fn_ip.own_ip); + } + else if (rc >= 0 && ip_version == 6) { + if(memcmp(&obp_tftp_args.ci6addr.addr, null_ip6, 16) != 0 + && memcmp(&obp_tftp_args.ci6addr.addr, &fn_ip.own_ip6, 16) != 0) + memcpy(&fn_ip.own_ip6, &obp_tftp_args.ci6addr.addr, 16); + + if(memcmp(&obp_tftp_args.si6addr.addr, null_ip6, 16) != 0 + && memcmp(&obp_tftp_args.si6addr.addr, &fn_ip.server_ip6.addr, 16) != 0) + memcpy(&fn_ip.server_ip6.addr, &obp_tftp_args.si6addr.addr, 16); + } + if (rc == -1) { + netload_error(0x3001, "Could not get IP address"); + close(fn_ip.fd); + rc = -101; + goto err_out; + } + + if (ip_version == 4) { + printf(" Using IPv4 address: %d.%d.%d.%d\n", + ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF), + ((fn_ip.own_ip >> 8) & 0xFF), ( fn_ip.own_ip & 0xFF)); + } else if (ip_version == 6) { + char ip6_str[40]; + ipv6_to_str(fn_ip.own_ip6.addr, ip6_str); + printf(" Using IPv6 address: %s\n", ip6_str); + } + + if (rc == -2) { + netload_error(0x3002, "ARP request to TFTP server " + "(%d.%d.%d.%d) failed", + ((fn_ip.server_ip >> 24) & 0xFF), + ((fn_ip.server_ip >> 16) & 0xFF), + ((fn_ip.server_ip >> 8) & 0xFF), + ( fn_ip.server_ip & 0xFF)); + close(fn_ip.fd); + rc = -102; + goto err_out; + } + if (rc == -4 || rc == -3) { + netload_error(0x3008, "Can't obtain TFTP server IP address"); + close(fn_ip.fd); + rc = -107; + goto err_out; + } + + /*********************************************************** + * + * Load file via TFTP into buffer provided by OpenFirmware + * + ***********************************************************/ + + if (obp_tftp_args.filename[0] != 0) { + strncpy(fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1); + fn_ip.filename[sizeof(fn_ip.filename)-1] = 0; + } + + fn_ip.ip_version = ip_version; + + if (ip_version == 4) { + printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n", + fn_ip.filename, + ((fn_ip.server_ip >> 24) & 0xFF), + ((fn_ip.server_ip >> 16) & 0xFF), + ((fn_ip.server_ip >> 8) & 0xFF), + ( fn_ip.server_ip & 0xFF)); + } else if (ip_version == 6) { + char ip6_str[40]; + printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename); + ipv6_to_str(fn_ip.server_ip6.addr, ip6_str); + printf("%s\n", ip6_str); + } + + /* Do the TFTP load and print error message if necessary */ + rc = 0; + filename_len = strlen(fn_ip.filename); + if (filename_len > 0 && fn_ip.filename[filename_len - 1] != '/' && + !fn_ip.pl_cfgfile) { + rc = tftp_load(&fn_ip, buffer, len, obp_tftp_args.tftp_retries); + } + + if (rc <= 0 && !obp_tftp_args.filename[0] && + (!filename_len || fn_ip.filename[filename_len - 1] == '/')) { + rc = net_pxelinux_load(&fn_ip, buffer, len, own_mac, + obp_tftp_args.tftp_retries); + } + + if (obp_tftp_args.ip_init == IP_INIT_DHCP) + dhcp_send_release(fn_ip.fd); + + close(fn_ip.fd); + + if (rc >= 0) { + encode_response(pkt_buffer, MAX_PKT_SIZE, obp_tftp_args.ip_init); + } + err_out: + SLOF_free_mem(pkt_buffer, MAX_PKT_SIZE); + free(fn_ip.pl_cfgfile); + free(fn_ip.pl_prefix); + return rc; +} |