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