1 /******************************************************************************
2  * Copyright (c) 2004, 2008 IBM Corporation
3  * All rights reserved.
4  * This program and the accompanying materials
5  * are made available under the terms of the BSD License
6  * which accompanies this distribution, and is available at
7  * http://www.opensource.org/licenses/bsd-license.php
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12 
13 #include <unistd.h>
14 #include <tftp.h>
15 #include <ethernet.h>
16 #include <dhcp.h>
17 #include <dhcpv6.h>
18 #include <ipv4.h>
19 #include <ipv6.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <time.h>
23 #include <stdlib.h>
24 #include <sys/socket.h>
25 #include <libbootmsg/libbootmsg.h>
26 #include <helpers.h>
27 #include "args.h"
28 #include "netapps.h"
29 #include "pxelinux.h"
30 
31 #define IP_INIT_DEFAULT 5
32 #define IP_INIT_NONE    0
33 #define IP_INIT_BOOTP   1
34 #define IP_INIT_DHCP    2
35 #define IP_INIT_DHCPV6_STATELESS    3
36 #define IP_INIT_IPV6_MANUAL         4
37 
38 #define MAX_PKT_SIZE         1720
39 #define DEFAULT_BOOT_RETRIES 10
40 #define DEFAULT_TFTP_RETRIES 20
41 static int ip_version;
42 
43 typedef struct {
44 	char filename[100];
45 	int  ip_init;
46 	char siaddr[4];
47 	ip6_addr_t si6addr;
48 	char ciaddr[4];
49 	ip6_addr_t ci6addr;
50 	char giaddr[4];
51 	ip6_addr_t gi6addr;
52 	int  bootp_retries;
53 	int  tftp_retries;
54 } obp_tftp_args_t;
55 
56 /**
57  * Print error with preceeding error code
58  */
netload_error(int errcode,const char * format,...)59 static void netload_error(int errcode, const char *format, ...)
60 {
61 	va_list vargs;
62 	char buf[256];
63 	int elen;
64 
65 	elen = sprintf(buf, "E%04X: (net) ", errcode);
66 
67 	va_start(vargs, format);
68 	vsnprintf(&buf[elen], sizeof(buf) - elen, format, vargs);
69 	va_end(vargs);
70 
71 	bootmsg_error(errcode, &buf[elen - 6]);
72 	write_mm_log(buf, strlen(buf), 0x91);
73 }
74 
75 /**
76  * Parses a argument string for IPv6 booting, extracts all
77  * parameters and fills a structure accordingly
78  *
79  * @param  arg_str        string with arguments, separated with ','
80  * @param  argc           number of arguments
81  * @param  obp_tftp_args  structure which contains the result
82  * @return                updated arg_str
83  */
84 static const char *
parse_ipv6args(const char * arg_str,unsigned int argc,obp_tftp_args_t * obp_tftp_args)85 parse_ipv6args (const char *arg_str, unsigned int argc,
86 		obp_tftp_args_t *obp_tftp_args)
87 {
88 	char *ptr = NULL;
89 	char arg_buf[100];
90 
91 	// find out siaddr
92 	if (argc == 0)
93 		memset(&obp_tftp_args->si6addr.addr, 0, 16);
94 	else {
95 		argncpy(arg_str, 0, arg_buf, 100);
96 		if(str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->si6addr.addr[0]))) {
97 			arg_str = get_arg_ptr(arg_str, 1);
98 			--argc;
99 		}
100 		else if(arg_buf[0] == 0) {
101 			memset(&obp_tftp_args->si6addr.addr, 0, 16);
102 			arg_str = get_arg_ptr(arg_str, 1);
103 			--argc;
104 		}
105 		else
106 			memset(&obp_tftp_args->si6addr.addr, 0, 16);
107 	}
108 
109 	// find out filename
110 	if (argc == 0)
111 		obp_tftp_args->filename[0] = 0;
112 	else {
113 		argncpy(arg_str, 0, obp_tftp_args->filename, 100);
114 		for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr)
115 			if(*ptr == '\\') {
116 				*ptr = '/';
117 			}
118 		arg_str = get_arg_ptr(arg_str, 1);
119 		--argc;
120 	}
121 
122 	// find out ciaddr
123 	if (argc == 0)
124 		memset(&obp_tftp_args->ci6addr, 0, 16);
125 	else {
126 		argncpy(arg_str, 0, arg_buf, 100);
127 		if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->ci6addr.addr[0]))) {
128 			arg_str = get_arg_ptr(arg_str, 1);
129 			--argc;
130 		}
131 		else if(arg_buf[0] == 0) {
132 			memset(&obp_tftp_args->ci6addr.addr, 0, 16);
133 			arg_str = get_arg_ptr(arg_str, 1);
134 			--argc;
135 		}
136 		else
137 			memset(&obp_tftp_args->ci6addr.addr, 0, 16);
138 	}
139 
140 	// find out giaddr
141 	if (argc == 0)
142 		memset(&obp_tftp_args->gi6addr, 0, 16);
143 	else {
144 		argncpy(arg_str, 0, arg_buf, 100);
145 		if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->gi6addr.addr)) ) {
146 			arg_str = get_arg_ptr(arg_str, 1);
147 			--argc;
148 		}
149 		else if(arg_buf[0] == 0) {
150 			memset(&obp_tftp_args->gi6addr, 0, 16);
151 			arg_str = get_arg_ptr(arg_str, 1);
152 			--argc;
153 		}
154 		else
155 			memset(&obp_tftp_args->gi6addr.addr, 0, 16);
156 	}
157 
158 	return arg_str;
159 }
160 
161 
162 /**
163  * Parses a argument string for IPv4 booting, extracts all
164  * parameters and fills a structure accordingly
165  *
166  * @param  arg_str        string with arguments, separated with ','
167  * @param  argc           number of arguments
168  * @param  obp_tftp_args  structure which contains the result
169  * @return                updated arg_str
170  */
171 static const char *
parse_ipv4args(const char * arg_str,unsigned int argc,obp_tftp_args_t * obp_tftp_args)172 parse_ipv4args (const char *arg_str, unsigned int argc,
173 		obp_tftp_args_t *obp_tftp_args)
174 {
175 	char *ptr = NULL;
176 	char arg_buf[100];
177 
178 	// find out siaddr
179 	if(argc==0) {
180 		memset(obp_tftp_args->siaddr, 0, 4);
181 	} else {
182 		argncpy(arg_str, 0, arg_buf, 100);
183 		if(strtoip(arg_buf, obp_tftp_args->siaddr)) {
184 			arg_str = get_arg_ptr(arg_str, 1);
185 			--argc;
186 		}
187 		else if(arg_buf[0] == 0) {
188 			memset(obp_tftp_args->siaddr, 0, 4);
189 			arg_str = get_arg_ptr(arg_str, 1);
190 			--argc;
191 		}
192 		else
193 			memset(obp_tftp_args->siaddr, 0, 4);
194 	}
195 
196 	// find out filename
197 	if(argc==0)
198 		obp_tftp_args->filename[0] = 0;
199 	else {
200 		argncpy(arg_str, 0, obp_tftp_args->filename, 100);
201 		for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr)
202 			if(*ptr == '\\')
203 				*ptr = '/';
204 		arg_str = get_arg_ptr(arg_str, 1);
205 		--argc;
206 	}
207 
208 	// find out ciaddr
209 	if(argc==0)
210 		memset(obp_tftp_args->ciaddr, 0, 4);
211 	else {
212 		argncpy(arg_str, 0, arg_buf, 100);
213 		if(strtoip(arg_buf, obp_tftp_args->ciaddr)) {
214 			arg_str = get_arg_ptr(arg_str, 1);
215 			--argc;
216 		}
217 		else if(arg_buf[0] == 0) {
218 			memset(obp_tftp_args->ciaddr, 0, 4);
219 			arg_str = get_arg_ptr(arg_str, 1);
220 			--argc;
221 		}
222 		else
223 			memset(obp_tftp_args->ciaddr, 0, 4);
224 	}
225 
226 	// find out giaddr
227 	if(argc==0)
228 		memset(obp_tftp_args->giaddr, 0, 4);
229 	else {
230 		argncpy(arg_str, 0, arg_buf, 100);
231 		if(strtoip(arg_buf, obp_tftp_args->giaddr)) {
232 			arg_str = get_arg_ptr(arg_str, 1);
233 			--argc;
234 		}
235 		else if(arg_buf[0] == 0) {
236 			memset(obp_tftp_args->giaddr, 0, 4);
237 			arg_str = get_arg_ptr(arg_str, 1);
238 			--argc;
239 		}
240 		else
241 			memset(obp_tftp_args->giaddr, 0, 4);
242 	}
243 
244 	return arg_str;
245 }
246 
247 /**
248  * Parses a argument string which is given by netload, extracts all
249  * parameters and fills a structure according to this
250  *
251  * Netload-Parameters:
252  *    [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
253  *
254  * @param  arg_str        string with arguments, separated with ','
255  * @param  obp_tftp_args  structure which contains the result
256  * @return                none
257  */
258 static void
parse_args(const char * arg_str,obp_tftp_args_t * obp_tftp_args)259 parse_args(const char *arg_str, obp_tftp_args_t *obp_tftp_args)
260 {
261 	unsigned int argc;
262 	char arg_buf[100];
263 
264 	memset(obp_tftp_args, 0, sizeof(*obp_tftp_args));
265 
266 	argc = get_args_count(arg_str);
267 
268 	// find out if we should use BOOTP or DHCP
269 	if(argc==0)
270 		obp_tftp_args->ip_init = IP_INIT_DEFAULT;
271 	else {
272 		argncpy(arg_str, 0, arg_buf, 100);
273 		if (strcasecmp(arg_buf, "bootp") == 0) {
274 			obp_tftp_args->ip_init = IP_INIT_BOOTP;
275 			arg_str = get_arg_ptr(arg_str, 1);
276 			--argc;
277 		}
278 		else if(strcasecmp(arg_buf, "dhcp") == 0) {
279 			obp_tftp_args->ip_init = IP_INIT_DHCP;
280 			arg_str = get_arg_ptr(arg_str, 1);
281 			--argc;
282 		}
283 		else if(strcasecmp(arg_buf, "ipv6") == 0) {
284 			obp_tftp_args->ip_init = IP_INIT_DHCPV6_STATELESS;
285 			arg_str = get_arg_ptr(arg_str, 1);
286 			--argc;
287 			ip_version = 6;
288 		}
289 		else
290 			obp_tftp_args->ip_init = IP_INIT_DEFAULT;
291 	}
292 
293 	if (ip_version == 4) {
294 		arg_str = parse_ipv4args (arg_str, argc, obp_tftp_args);
295 	}
296 	else if (ip_version == 6) {
297 		arg_str = parse_ipv6args (arg_str, argc, obp_tftp_args);
298 	}
299 
300 	// find out bootp-retries
301 	if (argc == 0)
302 		obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
303 	else {
304 		argncpy(arg_str, 0, arg_buf, 100);
305 		if(arg_buf[0] == 0)
306 			obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
307 		else {
308 			obp_tftp_args->bootp_retries = strtol(arg_buf, 0, 10);
309 			if(obp_tftp_args->bootp_retries < 0)
310 				obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
311 		}
312 		arg_str = get_arg_ptr(arg_str, 1);
313 		--argc;
314 	}
315 
316 	// find out tftp-retries
317 	if (argc == 0)
318 		obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
319 	else {
320 		argncpy(arg_str, 0, arg_buf, 100);
321 		if(arg_buf[0] == 0)
322 			obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
323 		else {
324 			obp_tftp_args->tftp_retries = strtol(arg_buf, 0, 10);
325 			if(obp_tftp_args->tftp_retries < 0)
326 				obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
327 		}
328 		arg_str = get_arg_ptr(arg_str, 1);
329 		--argc;
330 	}
331 }
332 
333 /**
334  * DHCP: Wrapper for obtaining IP and configuration info from DHCP server
335  *       for both IPv4 and IPv6.
336  *       (makes several attempts).
337  *
338  * @param  ret_buffer    buffer for returning BOOTP-REPLY packet data
339  * @param  fn_ip         contains the following configuration information:
340  *                       client MAC, client IP, TFTP-server MAC,
341  *                       TFTP-server IP, Boot file name
342  * @param  retries       No. of DHCP attempts
343  * @param  flags         flags for specifying type of dhcp attempt (IPv4/IPv6)
344  *                       ZERO   - attempt DHCPv4 followed by DHCPv6
345  *                       F_IPV4 - attempt only DHCPv4
346  *                       F_IPV6 - attempt only DHCPv6
347  * @return               ZERO - IP and configuration info obtained;
348  *                       NON ZERO - error condition occurs.
349  */
dhcp(char * ret_buffer,struct filename_ip * fn_ip,unsigned int retries,int flags)350 int dhcp(char *ret_buffer, struct filename_ip *fn_ip, unsigned int retries,
351 	 int flags)
352 {
353 	int i = (int) retries+1;
354 	int rc = -1;
355 
356 	printf("  Requesting information via DHCP%s:     ",
357 	       flags == F_IPV4 ? "v4" : flags == F_IPV6 ? "v6" : "");
358 
359 	if (flags != F_IPV6)
360 		dhcpv4_generate_transaction_id();
361 	if (flags != F_IPV4)
362 		dhcpv6_generate_transaction_id();
363 
364 	do {
365 		printf("\b\b\b%03d", i-1);
366 		if (getchar() == 27) {
367 			printf("\nAborted\n");
368 			return -1;
369 		}
370 		if (!--i) {
371 			printf("\nGiving up after %d DHCP requests\n", retries);
372 			return -1;
373 		}
374 		if (!flags || (flags == F_IPV4)) {
375 			ip_version = 4;
376 			rc = dhcpv4(ret_buffer, fn_ip);
377 		}
378 		if ((!flags && (rc == -1)) || (flags == F_IPV6)) {
379 			ip_version = 6;
380 			set_ipv6_address(fn_ip->fd, 0);
381 			rc = dhcpv6(ret_buffer, fn_ip);
382 			if (rc == 0) {
383 				memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16);
384 				break;
385 			}
386 
387 		}
388 		if (rc != -1) /* either success or non-dhcp failure */
389 			break;
390 	} while (1);
391 	printf("\b\b\b\bdone\n");
392 
393 	return rc;
394 }
395 
396 /**
397  * Seed the random number generator with our mac and current timestamp
398  */
seed_rng(uint8_t mac[])399 static void seed_rng(uint8_t mac[])
400 {
401 	unsigned int seed;
402 
403 	asm volatile("mftbl %0" : "=r"(seed));
404 	seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5];
405 	srand(seed);
406 }
407 
tftp_load(filename_ip_t * fnip,void * buffer,int len,unsigned int retries)408 static int tftp_load(filename_ip_t *fnip, void *buffer, int len,
409                      unsigned int retries)
410 {
411 	tftp_err_t tftp_err;
412 	int rc;
413 
414 	rc = tftp(fnip, buffer, len, retries, &tftp_err);
415 
416 	if (rc > 0) {
417 		printf("  TFTP: Received %s (%d KBytes)\n", fnip->filename,
418 		       rc / 1024);
419 	} else {
420 		int ecode;
421 		const char *errstr = NULL;
422 		rc = tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode);
423 		if (errstr)
424 			netload_error(ecode, errstr);
425 	}
426 
427 	return rc;
428 }
429 
get_uuid(void)430 static const char *get_uuid(void)
431 {
432 	char *addr;
433 	int len;
434 
435 	if (SLOF_get_property("/", "system-id", &addr, &len))
436 		return NULL;
437 	if (len < 37) {    /* This should never happen... */
438 		puts("Warning: UUID property is too short.");
439 		return NULL;
440 	}
441 
442 	return addr;
443 }
444 
445 #define CFG_BUF_SIZE 2048
446 #define MAX_PL_CFG_ENTRIES 16
net_pxelinux_load(filename_ip_t * fnip,char * loadbase,int maxloadlen,uint8_t * mac,int retries)447 static int net_pxelinux_load(filename_ip_t *fnip, char *loadbase,
448                              int maxloadlen, uint8_t *mac, int retries)
449 {
450 	struct pl_cfg_entry entries[MAX_PL_CFG_ENTRIES];
451 	int def, rc, ilen;
452 	static char *cfgbuf;
453 
454 	cfgbuf = malloc(CFG_BUF_SIZE);
455 	if (!cfgbuf) {
456 		puts("Not enough memory for pxelinux config file buffer!");
457 		return -1;
458 	}
459 
460 	rc = pxelinux_load_parse_cfg(fnip, mac, get_uuid(), retries,
461 	                             cfgbuf, CFG_BUF_SIZE,
462 	                             entries, MAX_PL_CFG_ENTRIES, &def);
463 	if (rc < 0)
464 		goto out_free;
465 	if (rc == 0) {
466 		puts("No valid entries in pxelinux config file.");
467 		rc = -1;
468 		goto out_free;
469 	}
470 
471 	/* Load kernel */
472 	strncpy(fnip->filename, entries[def].kernel,
473 		sizeof(fnip->filename) - 1);
474 	fnip->filename[sizeof(fnip->filename) - 1] = 0;
475 	rc = tftp_load(fnip, loadbase, maxloadlen, retries);
476 	if (rc <= 0)
477 		goto out_free;
478 
479 	/* Load ramdisk */
480 	if (entries[def].initrd) {
481 		loadbase += rc;
482 		maxloadlen -= rc;
483 		if (maxloadlen <= 0) {
484 			puts("  Not enough space for loading the initrd!");
485 			rc = -1;
486 			goto out_free;
487 		}
488 		strncpy(fnip->filename, entries[def].initrd,
489 			sizeof(fnip->filename) - 1);
490 		ilen = tftp_load(fnip, loadbase, maxloadlen, retries);
491 		if (ilen < 0) {
492 			rc = ilen;
493 			goto out_free;
494 		}
495 		/* The ELF loader will move the kernel to some spot in low mem
496 		 * later, thus move the initrd to the end of the RAM instead */
497 		memmove(loadbase + maxloadlen - ilen, loadbase, ilen);
498 		/* Encode the initrd information in the device tree */
499 		SLOF_set_chosen_int("linux,initrd-start",
500 		                    (long)loadbase + maxloadlen - ilen);
501 		SLOF_set_chosen_int("linux,initrd-end",
502 		                    (long)loadbase + maxloadlen);
503 	}
504 
505 	if (entries[def].append) {
506 		SLOF_set_chosen_bytes("bootargs", entries[def].append,
507 		                      strlen(entries[def].append) + 1);
508 	}
509 
510 out_free:
511 	free(cfgbuf);
512 	return rc;
513 }
514 
encode_response(char * pkt_buffer,size_t size,int ip_init)515 static void encode_response(char *pkt_buffer, size_t size, int ip_init)
516 {
517 	switch(ip_init) {
518 	case IP_INIT_BOOTP:
519 		SLOF_encode_bootp_response(pkt_buffer, size);
520 		break;
521 	case IP_INIT_DHCP:
522 	case IP_INIT_DHCPV6_STATELESS:
523 	case IP_INIT_DEFAULT:
524 		SLOF_encode_dhcp_response(pkt_buffer, size);
525 		break;
526 	default:
527 		break;
528 	}
529 }
530 
netload(char * buffer,int len,char * args_fs,int alen)531 int netload(char *buffer, int len, char *args_fs, int alen)
532 {
533 	int rc, filename_len;
534 	filename_ip_t fn_ip;
535 	int fd_device;
536 	obp_tftp_args_t obp_tftp_args;
537 	char null_ip[4] = { 0x00, 0x00, 0x00, 0x00 };
538 	char null_ip6[16] = { 0x00, 0x00, 0x00, 0x00,
539 			     0x00, 0x00, 0x00, 0x00,
540 			     0x00, 0x00, 0x00, 0x00,
541 			     0x00, 0x00, 0x00, 0x00 };
542 	uint8_t own_mac[6];
543 	char *pkt_buffer;
544 
545 	ip_version = 4;
546 
547 	pkt_buffer = SLOF_alloc_mem(MAX_PKT_SIZE);
548 	if (!pkt_buffer) {
549 		puts("ERROR: Unable to allocate memory");
550 		return -1;
551 	}
552 	memset(pkt_buffer, 0, MAX_PKT_SIZE);
553 
554 	puts("\n Initializing NIC");
555 	memset(&fn_ip, 0, sizeof(filename_ip_t));
556 
557 	/***********************************************************
558 	 *
559 	 * Initialize network stuff and retrieve boot informations
560 	 *
561 	 ***********************************************************/
562 
563 	/* Wait for link up and get mac_addr from device */
564 	for(rc=0; rc<DEFAULT_BOOT_RETRIES; ++rc) {
565 		if(rc > 0) {
566 			set_timer(TICKS_SEC);
567 			while (get_timer() > 0);
568 		}
569 		fd_device = socket(0, 0, 0, (char*) own_mac);
570 		if(fd_device != -2)
571 			break;
572 		if(getchar() == 27) {
573 			fd_device = -2;
574 			break;
575 		}
576 	}
577 
578 	if (fd_device == -1) {
579 		netload_error(0x3000, "Could not read MAC address");
580 		rc = -100;
581 		goto err_out;
582 	}
583 	else if (fd_device == -2) {
584 		netload_error(0x3006, "Could not initialize network device");
585 		rc = -101;
586 		goto err_out;
587 	}
588 
589 	fn_ip.fd = fd_device;
590 
591 	printf("  Reading MAC address from device: "
592 	       "%02x:%02x:%02x:%02x:%02x:%02x\n",
593 	       own_mac[0], own_mac[1], own_mac[2],
594 	       own_mac[3], own_mac[4], own_mac[5]);
595 
596 	// init ethernet layer
597 	set_mac_address(own_mac);
598 
599 	seed_rng(own_mac);
600 
601 	if (alen > 0) {
602 		char args[256];
603 		if (alen > sizeof(args) - 1) {
604 			puts("ERROR: Parameter string is too long.");
605 			rc = -7;
606 			goto err_out;
607 		}
608 		/* Convert forth string into NUL-terminated C-string */
609 		strncpy(args, args_fs, alen);
610 		args[alen] = 0;
611 		parse_args(args, &obp_tftp_args);
612 		if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES)
613 			obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
614 		else
615 			obp_tftp_args.bootp_retries -= rc;
616 	}
617 	else {
618 		memset(&obp_tftp_args, 0, sizeof(obp_tftp_args_t));
619 		obp_tftp_args.ip_init = IP_INIT_DEFAULT;
620 		obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
621 		obp_tftp_args.tftp_retries = DEFAULT_TFTP_RETRIES;
622 	}
623 	memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4);
624 
625 	//  reset of error code
626 	rc = 0;
627 
628 	/* if we still have got all necessary parameters, then we don't
629 	   need to perform an BOOTP/DHCP-Request */
630 	if (ip_version == 4) {
631 		if (memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0
632 		    && memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0
633 		    && obp_tftp_args.filename[0] != 0) {
634 
635 			memcpy(&fn_ip.server_ip, &obp_tftp_args.siaddr, 4);
636 			obp_tftp_args.ip_init = IP_INIT_NONE;
637 		}
638 	}
639 	else if (ip_version == 6) {
640 		if (memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0
641 		    && obp_tftp_args.filename[0] != 0) {
642 			memcpy(&fn_ip.server_ip6.addr[0],
643 			       &obp_tftp_args.si6addr.addr, 16);
644 			obp_tftp_args.ip_init = IP_INIT_IPV6_MANUAL;
645 		}
646 		else {
647 			obp_tftp_args.ip_init = IP_INIT_DHCPV6_STATELESS;
648 		}
649 	}
650 
651 	// construction of fn_ip from parameter
652 	switch(obp_tftp_args.ip_init) {
653 	case IP_INIT_BOOTP:
654 		// if giaddr in not specified, then we have to identify
655 		// the BOOTP server via broadcasts
656 		if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) {
657 			// don't do this, when using DHCP !!!
658 			fn_ip.server_ip = 0xFFFFFFFF;
659 		}
660 		// if giaddr is specified, then we have to use this
661 		// IP address as proxy to identify the BOOTP server
662 		else {
663 			memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4);
664 		}
665 		rc = bootp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries);
666 		break;
667 	case IP_INIT_DHCP:
668 		rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4);
669 		break;
670 	case IP_INIT_DHCPV6_STATELESS:
671 		rc = dhcp(pkt_buffer, &fn_ip,
672 			  obp_tftp_args.bootp_retries, F_IPV6);
673 		break;
674 	case IP_INIT_IPV6_MANUAL:
675 		if (memcmp(&obp_tftp_args.ci6addr, null_ip6, 16)) {
676 			set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr);
677 		} else {
678 			/*
679 			 * If no client address has been specified, then
680 			 * use a link-local or stateless autoconfig address
681 			 */
682 			set_ipv6_address(fn_ip.fd, NULL);
683 			memcpy(&fn_ip.own_ip6, get_ipv6_address(), 16);
684 		}
685 		break;
686 	case IP_INIT_DEFAULT:
687 		rc = dhcp(pkt_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0);
688 		break;
689 	case IP_INIT_NONE:
690 	default:
691 		break;
692 	}
693 
694 	if(rc >= 0 && ip_version == 4) {
695 		if(memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0
696 		&& memcmp(obp_tftp_args.ciaddr, &fn_ip.own_ip, 4) != 0)
697 			memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4);
698 
699 		if(memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0
700 		&& memcmp(obp_tftp_args.siaddr, &fn_ip.server_ip, 4) != 0)
701 			memcpy(&fn_ip.server_ip, obp_tftp_args.siaddr, 4);
702 
703 		// init IPv4 layer
704 		set_ipv4_address(fn_ip.own_ip);
705 	}
706 	else if (rc >= 0 && ip_version == 6) {
707 		if(memcmp(&obp_tftp_args.ci6addr.addr, null_ip6, 16) != 0
708 		&& memcmp(&obp_tftp_args.ci6addr.addr, &fn_ip.own_ip6, 16) != 0)
709 			memcpy(&fn_ip.own_ip6, &obp_tftp_args.ci6addr.addr, 16);
710 
711 		if(memcmp(&obp_tftp_args.si6addr.addr, null_ip6, 16) != 0
712 		&& memcmp(&obp_tftp_args.si6addr.addr, &fn_ip.server_ip6.addr, 16) != 0)
713 			memcpy(&fn_ip.server_ip6.addr, &obp_tftp_args.si6addr.addr, 16);
714 	}
715 	if (rc == -1) {
716 		netload_error(0x3001, "Could not get IP address");
717 		close(fn_ip.fd);
718 		rc = -101;
719 		goto err_out;
720 	}
721 
722 	if (ip_version == 4) {
723 		printf("  Using IPv4 address: %d.%d.%d.%d\n",
724 			((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF),
725 			((fn_ip.own_ip >>  8) & 0xFF), ( fn_ip.own_ip        & 0xFF));
726 	} else if (ip_version == 6) {
727 		char ip6_str[40];
728 		ipv6_to_str(fn_ip.own_ip6.addr, ip6_str);
729 		printf("  Using IPv6 address: %s\n", ip6_str);
730 	}
731 
732 	if (rc == -2) {
733 		netload_error(0x3002, "ARP request to TFTP server "
734 			"(%d.%d.%d.%d) failed",
735 			((fn_ip.server_ip >> 24) & 0xFF),
736 			((fn_ip.server_ip >> 16) & 0xFF),
737 			((fn_ip.server_ip >>  8) & 0xFF),
738 			( fn_ip.server_ip        & 0xFF));
739 		close(fn_ip.fd);
740 		rc = -102;
741 		goto err_out;
742 	}
743 	if (rc == -4 || rc == -3) {
744 		netload_error(0x3008, "Can't obtain TFTP server IP address");
745 		close(fn_ip.fd);
746 		rc = -107;
747 		goto err_out;
748 	}
749 
750 	/***********************************************************
751 	 *
752 	 * Load file via TFTP into buffer provided by OpenFirmware
753 	 *
754 	 ***********************************************************/
755 
756 	if (obp_tftp_args.filename[0] != 0) {
757 		strncpy(fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1);
758 		fn_ip.filename[sizeof(fn_ip.filename)-1] = 0;
759 	}
760 
761 	fn_ip.ip_version = ip_version;
762 
763 	if (ip_version == 4) {
764 		printf("  Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n",
765 			fn_ip.filename,
766 			((fn_ip.server_ip >> 24) & 0xFF),
767 			((fn_ip.server_ip >> 16) & 0xFF),
768 			((fn_ip.server_ip >>  8) & 0xFF),
769 			( fn_ip.server_ip        & 0xFF));
770 	} else if (ip_version == 6) {
771 		char ip6_str[40];
772 		printf("  Requesting file \"%s\" via TFTP from ", fn_ip.filename);
773 		ipv6_to_str(fn_ip.server_ip6.addr, ip6_str);
774 		printf("%s\n", ip6_str);
775 	}
776 
777 	/* Do the TFTP load and print error message if necessary */
778 	rc = 0;
779 	filename_len = strlen(fn_ip.filename);
780 	if (filename_len > 0 && fn_ip.filename[filename_len - 1] != '/' &&
781 	    !fn_ip.pl_cfgfile) {
782 		rc = tftp_load(&fn_ip, buffer, len, obp_tftp_args.tftp_retries);
783 	}
784 
785 	if (rc <= 0 && !obp_tftp_args.filename[0] &&
786 	    (!filename_len || fn_ip.filename[filename_len - 1] == '/')) {
787 		rc = net_pxelinux_load(&fn_ip, buffer, len, own_mac,
788 		                       obp_tftp_args.tftp_retries);
789 	}
790 
791 	if (obp_tftp_args.ip_init == IP_INIT_DHCP)
792 		dhcp_send_release(fn_ip.fd);
793 
794 	close(fn_ip.fd);
795 
796 	if (rc >= 0) {
797 		encode_response(pkt_buffer, MAX_PKT_SIZE, obp_tftp_args.ip_init);
798 	}
799   err_out:
800 	SLOF_free_mem(pkt_buffer, MAX_PKT_SIZE);
801 	free(fn_ip.pl_cfgfile);
802 	free(fn_ip.pl_prefix);
803 	return rc;
804 }
805