aboutsummaryrefslogtreecommitdiffstats
path: root/roms/SLOF/lib/libnet/netload.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/SLOF/lib/libnet/netload.c')
-rw-r--r--roms/SLOF/lib/libnet/netload.c804
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;
+}