These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / SLOF / clients / net-snk / app / netapps / netboot.c
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 <netlib/tftp.h>
14 #include <netlib/ethernet.h>
15 #include <netlib/dhcp.h>
16 #include <netlib/dhcpv6.h>
17 #include <netlib/ipv4.h>
18 #include <netlib/ipv6.h>
19 #include <netlib/dns.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <time.h>
23 #include <stdlib.h>
24 #include <sys/socket.h>
25 #include <netapps/args.h>
26 #include <libbootmsg/libbootmsg.h>
27 #include <of.h>
28 #include "netapps.h"
29
30 #define IP_INIT_DEFAULT 5
31 #define IP_INIT_NONE    0
32 #define IP_INIT_BOOTP   1
33 #define IP_INIT_DHCP    2
34 #define IP_INIT_DHCPV6_STATELESS    3
35 #define IP_INIT_IPV6_MANUAL         4
36
37 #define DEFAULT_BOOT_RETRIES 10
38 #define DEFAULT_TFTP_RETRIES 20
39 static int ip_version = 4;
40
41 typedef struct {
42         char filename[100];
43         int  ip_init;
44         char siaddr[4];
45         ip6_addr_t si6addr;
46         char ciaddr[4];
47         ip6_addr_t ci6addr;
48         char giaddr[4];
49         ip6_addr_t gi6addr;
50         int  bootp_retries;
51         int  tftp_retries;
52 } obp_tftp_args_t;
53
54
55 /**
56  * Parses a argument string for IPv6 booting, extracts all
57  * parameters and fills a structure accordingly
58  *
59  * @param  arg_str        string with arguments, separated with ','
60  * @param  argc           number of arguments
61  * @param  obp_tftp_args  structure which contains the result
62  * @return                updated arg_str
63  */
64 static const char * 
65 parse_ipv6args (const char *arg_str, unsigned int argc,
66                 obp_tftp_args_t *obp_tftp_args)
67 {
68         char *ptr = NULL;
69         char arg_buf[100];
70
71         // find out siaddr
72         if (argc == 0)
73                 memset(&obp_tftp_args->si6addr.addr, 0, 16);
74         else {
75                 argncpy(arg_str, 0, arg_buf, 100);
76                 if(str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->si6addr.addr[0]))) {
77                         arg_str = get_arg_ptr(arg_str, 1);
78                         --argc;
79                 }
80                 else if(arg_buf[0] == 0) {
81                         memset(&obp_tftp_args->si6addr.addr, 0, 16);
82                         arg_str = get_arg_ptr(arg_str, 1);
83                         --argc;
84                 }
85                 else
86                         memset(&obp_tftp_args->si6addr.addr, 0, 16);
87         }
88
89         // find out filename
90         if (argc == 0)
91                 obp_tftp_args->filename[0] = 0;
92         else {
93                 argncpy(arg_str, 0, obp_tftp_args->filename, 100);
94                 for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr)
95                         if(*ptr == '\\') {
96                                 *ptr = '/';
97                         }
98                 arg_str = get_arg_ptr(arg_str, 1);
99                 --argc;
100         }
101
102         // find out ciaddr
103         if (argc == 0)
104                 memset(&obp_tftp_args->ci6addr, 0, 16);
105         else {
106                 argncpy(arg_str, 0, arg_buf, 100);
107                 if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->ci6addr.addr[0]))) {
108                         arg_str = get_arg_ptr(arg_str, 1);
109                         --argc;
110                 }
111                 else if(arg_buf[0] == 0) {
112                         memset(&obp_tftp_args->ci6addr.addr, 0, 16);
113                         arg_str = get_arg_ptr(arg_str, 1);
114                         --argc;
115                 }
116                 else
117                         memset(&obp_tftp_args->ci6addr.addr, 0, 16);
118         }
119
120         // find out giaddr
121         if (argc == 0)
122                 memset(&obp_tftp_args->gi6addr, 0, 16);
123         else {
124                 argncpy(arg_str, 0, arg_buf, 100);
125                 if (str_to_ipv6(arg_buf, (uint8_t *) &(obp_tftp_args->gi6addr.addr)) ) {
126                         arg_str = get_arg_ptr(arg_str, 1);
127                         --argc;
128                 }
129                 else if(arg_buf[0] == 0) {
130                         memset(&obp_tftp_args->gi6addr, 0, 16);
131                         arg_str = get_arg_ptr(arg_str, 1);
132                         --argc;
133                 }
134                 else
135                         memset(&obp_tftp_args->gi6addr.addr, 0, 16);
136         }
137
138         return arg_str;
139 }
140
141
142 /**
143  * Parses a argument string for IPv4 booting, extracts all
144  * parameters and fills a structure accordingly
145  *
146  * @param  arg_str        string with arguments, separated with ','
147  * @param  argc           number of arguments
148  * @param  obp_tftp_args  structure which contains the result
149  * @return                updated arg_str
150  */
151 static const char * 
152 parse_ipv4args (const char *arg_str, unsigned int argc,
153                 obp_tftp_args_t *obp_tftp_args)
154 {
155         char *ptr = NULL;
156         char arg_buf[100];
157
158         // find out siaddr
159         if(argc==0) {
160                 memset(obp_tftp_args->siaddr, 0, 4);
161         } else {
162                 argncpy(arg_str, 0, arg_buf, 100);
163                 if(strtoip(arg_buf, obp_tftp_args->siaddr)) {
164                         arg_str = get_arg_ptr(arg_str, 1);
165                         --argc;
166                 }
167                 else if(arg_buf[0] == 0) {
168                         memset(obp_tftp_args->siaddr, 0, 4);
169                         arg_str = get_arg_ptr(arg_str, 1);
170                         --argc;
171                 }
172                 else
173                         memset(obp_tftp_args->siaddr, 0, 4);
174         }
175
176         // find out filename
177         if(argc==0)
178                 obp_tftp_args->filename[0] = 0;
179         else {
180                 argncpy(arg_str, 0, obp_tftp_args->filename, 100);
181                 for(ptr = obp_tftp_args->filename; *ptr != 0; ++ptr)
182                         if(*ptr == '\\')
183                                 *ptr = '/';
184                 arg_str = get_arg_ptr(arg_str, 1);
185                 --argc;
186         }
187
188         // find out ciaddr
189         if(argc==0)
190                 memset(obp_tftp_args->ciaddr, 0, 4);
191         else {
192                 argncpy(arg_str, 0, arg_buf, 100);
193                 if(strtoip(arg_buf, obp_tftp_args->ciaddr)) {
194                         arg_str = get_arg_ptr(arg_str, 1);
195                         --argc;
196                 }
197                 else if(arg_buf[0] == 0) {
198                         memset(obp_tftp_args->ciaddr, 0, 4);
199                         arg_str = get_arg_ptr(arg_str, 1);
200                         --argc;
201                 }
202                 else
203                         memset(obp_tftp_args->ciaddr, 0, 4);
204         }
205
206         // find out giaddr
207         if(argc==0)
208                 memset(obp_tftp_args->giaddr, 0, 4);
209         else {
210                 argncpy(arg_str, 0, arg_buf, 100);
211                 if(strtoip(arg_buf, obp_tftp_args->giaddr)) {
212                         arg_str = get_arg_ptr(arg_str, 1);
213                         --argc;
214                 }
215                 else if(arg_buf[0] == 0) {
216                         memset(obp_tftp_args->giaddr, 0, 4);
217                         arg_str = get_arg_ptr(arg_str, 1);
218                         --argc;
219                 }
220                 else
221                         memset(obp_tftp_args->giaddr, 0, 4);
222         }
223
224         return arg_str;
225 }
226
227 /**
228  * Parses a argument string which is given by netload, extracts all
229  * parameters and fills a structure according to this
230  *
231  * Netload-Parameters:
232  *    [bootp,]siaddr,filename,ciaddr,giaddr,bootp-retries,tftp-retries
233  *
234  * @param  arg_str        string with arguments, separated with ','
235  * @param  obp_tftp_args  structure which contains the result
236  * @return                none
237  */
238 static void
239 parse_args(const char *arg_str, obp_tftp_args_t *obp_tftp_args)
240 {
241         unsigned int argc;
242         char arg_buf[100];
243
244         memset(obp_tftp_args, 0, sizeof(*obp_tftp_args));
245
246         argc = get_args_count(arg_str);
247
248         // find out if we should use BOOTP or DHCP
249         if(argc==0)
250                 obp_tftp_args->ip_init = IP_INIT_DEFAULT;
251         else {
252                 argncpy(arg_str, 0, arg_buf, 100);
253                 if (strcasecmp(arg_buf, "bootp") == 0) {
254                         obp_tftp_args->ip_init = IP_INIT_BOOTP;
255                         arg_str = get_arg_ptr(arg_str, 1);
256                         --argc;
257                 }
258                 else if(strcasecmp(arg_buf, "dhcp") == 0) {
259                         obp_tftp_args->ip_init = IP_INIT_DHCP;
260                         arg_str = get_arg_ptr(arg_str, 1);
261                         --argc;
262                 }
263                 else if(strcasecmp(arg_buf, "ipv6") == 0) {
264                         obp_tftp_args->ip_init = IP_INIT_DHCPV6_STATELESS;
265                         arg_str = get_arg_ptr(arg_str, 1);
266                         --argc;
267                         ip_version = 6;
268                 }
269                 else
270                         obp_tftp_args->ip_init = IP_INIT_DEFAULT;
271         }
272
273         if (ip_version == 4) {
274                 arg_str = parse_ipv4args (arg_str, argc, obp_tftp_args);
275         }
276         else if (ip_version == 6) {
277                 arg_str = parse_ipv6args (arg_str, argc, obp_tftp_args);
278         }
279
280         // find out bootp-retries
281         if (argc == 0)
282                 obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
283         else {
284                 argncpy(arg_str, 0, arg_buf, 100);
285                 if(arg_buf[0] == 0)
286                         obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
287                 else {
288                         obp_tftp_args->bootp_retries = strtol(arg_buf, 0, 10);
289                         if(obp_tftp_args->bootp_retries < 0)
290                                 obp_tftp_args->bootp_retries = DEFAULT_BOOT_RETRIES;
291                 }
292                 arg_str = get_arg_ptr(arg_str, 1);
293                 --argc;
294         }
295
296         // find out tftp-retries
297         if (argc == 0)
298                 obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
299         else {
300                 argncpy(arg_str, 0, arg_buf, 100);
301                 if(arg_buf[0] == 0)
302                         obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
303                 else {
304                         obp_tftp_args->tftp_retries = strtol(arg_buf, 0, 10);
305                         if(obp_tftp_args->tftp_retries < 0)
306                                 obp_tftp_args->tftp_retries = DEFAULT_TFTP_RETRIES;
307                 }
308                 arg_str = get_arg_ptr(arg_str, 1);
309                 --argc;
310         }
311 }
312
313 /**
314  * DHCP: Wrapper for obtaining IP and configuration info from DHCP server
315  *       for both IPv4 and IPv6.
316  *       (makes several attempts).
317  *
318  * @param  ret_buffer    buffer for returning BOOTP-REPLY packet data
319  * @param  fn_ip         contains the following configuration information:
320  *                       client MAC, client IP, TFTP-server MAC,
321  *                       TFTP-server IP, Boot file name
322  * @param  retries       No. of DHCP attempts
323  * @param  flags         flags for specifying type of dhcp attempt (IPv4/IPv6)
324  *                       ZERO   - attempt DHCPv4 followed by DHCPv6
325  *                       F_IPV4 - attempt only DHCPv4
326  *                       F_IPV6 - attempt only DHCPv6
327  * @return               ZERO - IP and configuration info obtained;
328  *                       NON ZERO - error condition occurs.
329  */
330 int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flags)
331 {
332         int i = (int) retries+1;
333         int rc = -1;
334
335         printf("  Requesting information via DHCP%s:     ",
336                flags == F_IPV4 ? "v4" : flags == F_IPV6 ? "v6" : "");
337
338         if (flags != F_IPV6)
339                 dhcpv4_generate_transaction_id();
340         if (flags != F_IPV4)
341                 dhcpv6_generate_transaction_id();
342
343         do {
344                 printf("\b\b\b%03d", i-1);
345                 if (getchar() == 27) {
346                         printf("\nAborted\n");
347                         return -1;
348                 }
349                 if (!--i) {
350                         printf("\nGiving up after %d DHCP requests\n", retries);
351                         return -1;
352                 }
353                 if (!flags || (flags == F_IPV4)) {
354                         ip_version = 4;
355                         rc = dhcpv4(ret_buffer, fn_ip);
356                 }
357                 if ((!flags && (rc == -1)) || (flags == F_IPV6)) {
358                         ip_version = 6;
359                         set_ipv6_address(fn_ip->fd, 0);
360                         rc = dhcpv6(ret_buffer, fn_ip);
361                         if (rc == 0) {
362                                 memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16);
363                                 break;
364                         }
365
366                 }
367                 if (rc != -1) /* either success or non-dhcp failure */
368                         break;
369         } while (1);
370         printf("\b\b\b\bdone\n");
371
372         return rc;
373 }
374
375 /**
376  * Seed the random number generator with our mac and current timestamp
377  */
378 static void seed_rng(uint8_t mac[])
379 {
380         unsigned int seed;
381
382         asm volatile("mftbl %0" : "=r"(seed));
383         seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5];
384         srand(seed);
385 }
386
387 int
388 netboot(int argc, char *argv[])
389 {
390         char buf[256];
391         int rc;
392         int len = strtol(argv[2], 0, 16);
393         char *buffer = (char *) strtol(argv[1], 0, 16);
394         char *ret_buffer = (char *) strtol(argv[3], 0, 16);
395         filename_ip_t fn_ip;
396         int fd_device;
397         tftp_err_t tftp_err;
398         obp_tftp_args_t obp_tftp_args;
399         char null_ip[4] = { 0x00, 0x00, 0x00, 0x00 };
400         char null_ip6[16] = { 0x00, 0x00, 0x00, 0x00,
401                              0x00, 0x00, 0x00, 0x00,
402                              0x00, 0x00, 0x00, 0x00, 
403                              0x00, 0x00, 0x00, 0x00 };
404         int huge_load = strtol(argv[4], 0, 10);
405         int32_t block_size = strtol(argv[5], 0, 10);
406         uint8_t own_mac[6];
407
408         puts("\n Initializing NIC");
409         memset(&fn_ip, 0, sizeof(filename_ip_t));
410
411         /***********************************************************
412          *
413          * Initialize network stuff and retrieve boot informations
414          *
415          ***********************************************************/
416
417         /* Wait for link up and get mac_addr from device */
418         for(rc=0; rc<DEFAULT_BOOT_RETRIES; ++rc) {
419                 if(rc > 0) {
420                         set_timer(TICKS_SEC);
421                         while (get_timer() > 0);
422                 }
423                 fd_device = socket(0, 0, 0, (char*) own_mac);
424                 if(fd_device != -2)
425                         break;
426                 if(getchar() == 27) {
427                         fd_device = -2;
428                         break;
429                 }
430         }
431
432         if (fd_device == -1) {
433                 strcpy(buf,"E3000: (net) Could not read MAC address");
434                 bootmsg_error(0x3000, &buf[7]);
435
436                 write_mm_log(buf, strlen(buf), 0x91);
437                 return -100;
438         }
439         else if (fd_device == -2) {
440                 strcpy(buf,"E3006: (net) Could not initialize network device");
441                 bootmsg_error(0x3006, &buf[7]);
442
443                 write_mm_log(buf, strlen(buf), 0x91);
444                 return -101;
445         }
446
447         fn_ip.fd = fd_device;
448
449         printf("  Reading MAC address from device: "
450                "%02x:%02x:%02x:%02x:%02x:%02x\n",
451                own_mac[0], own_mac[1], own_mac[2],
452                own_mac[3], own_mac[4], own_mac[5]);
453
454         // init ethernet layer
455         set_mac_address(own_mac);
456
457         seed_rng(own_mac);
458
459         if (argc > 6) {
460                 parse_args(argv[6], &obp_tftp_args);
461                 if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES)
462                         obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
463                 else
464                         obp_tftp_args.bootp_retries -= rc;
465         }
466         else {
467                 memset(&obp_tftp_args, 0, sizeof(obp_tftp_args_t));
468                 obp_tftp_args.ip_init = IP_INIT_DEFAULT;
469                 obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
470                 obp_tftp_args.tftp_retries = DEFAULT_TFTP_RETRIES;
471         }
472         memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4);
473
474         //  reset of error code
475         rc = 0;
476
477         /* if we still have got all necessary parameters, then we don't
478            need to perform an BOOTP/DHCP-Request */
479         if (ip_version == 4) {
480                 if (memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0
481                     && memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0
482                     && obp_tftp_args.filename[0] != 0) {
483
484                         memcpy(&fn_ip.server_ip, &obp_tftp_args.siaddr, 4);
485                         obp_tftp_args.ip_init = IP_INIT_NONE;
486                 }
487         }
488         else if (ip_version == 6) {
489                 if (memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0
490                     && obp_tftp_args.filename[0] != 0) {
491                         memcpy(&fn_ip.server_ip6.addr[0],
492                                &obp_tftp_args.si6addr.addr, 16);
493                         obp_tftp_args.ip_init = IP_INIT_IPV6_MANUAL;
494                 }
495                 else {
496                         obp_tftp_args.ip_init = IP_INIT_DHCPV6_STATELESS;
497                 }
498         }
499
500         // construction of fn_ip from parameter
501         switch(obp_tftp_args.ip_init) {
502         case IP_INIT_BOOTP:
503                 // if giaddr in not specified, then we have to identify
504                 // the BOOTP server via broadcasts
505                 if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) {
506                         // don't do this, when using DHCP !!!
507                         fn_ip.server_ip = 0xFFFFFFFF;
508                 }
509                 // if giaddr is specified, then we have to use this
510                 // IP address as proxy to identify the BOOTP server
511                 else {
512                         memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4);
513                 }
514                 rc = bootp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries);
515                 break;
516         case IP_INIT_DHCP:
517                 rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4);
518                 break;
519         case IP_INIT_DHCPV6_STATELESS:
520                 rc = dhcp(ret_buffer, &fn_ip,
521                           obp_tftp_args.bootp_retries, F_IPV6);
522                 break;
523         case IP_INIT_IPV6_MANUAL:
524                 if (memcmp(&obp_tftp_args.ci6addr, null_ip6, 16)) {
525                         set_ipv6_address(fn_ip.fd, &obp_tftp_args.ci6addr);
526                 } else {
527                         /*
528                          * If no client address has been specified, then
529                          * use a link-local or stateless autoconfig address
530                          */
531                         set_ipv6_address(fn_ip.fd, NULL);
532                         memcpy(&fn_ip.own_ip6, get_ipv6_address(), 16);
533                 }
534                 break;
535         case IP_INIT_DEFAULT:
536                 rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, 0);
537                 break;
538         case IP_INIT_NONE:
539         default:
540                 break;
541         }
542
543         if(rc >= 0 && ip_version == 4) {
544                 if(memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0
545                 && memcmp(obp_tftp_args.ciaddr, &fn_ip.own_ip, 4) != 0)
546                         memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4);
547
548                 if(memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0
549                 && memcmp(obp_tftp_args.siaddr, &fn_ip.server_ip, 4) != 0)
550                         memcpy(&fn_ip.server_ip, obp_tftp_args.siaddr, 4);
551
552                 // init IPv4 layer
553                 set_ipv4_address(fn_ip.own_ip);
554         }
555         else if (rc >= 0 && ip_version == 6) {
556                 if(memcmp(&obp_tftp_args.ci6addr.addr, null_ip6, 16) != 0
557                 && memcmp(&obp_tftp_args.ci6addr.addr, &fn_ip.own_ip6, 16) != 0)
558                         memcpy(&fn_ip.own_ip6, &obp_tftp_args.ci6addr.addr, 16);
559
560                 if(memcmp(&obp_tftp_args.si6addr.addr, null_ip6, 16) != 0
561                 && memcmp(&obp_tftp_args.si6addr.addr, &fn_ip.server_ip6.addr, 16) != 0)
562                         memcpy(&fn_ip.server_ip6.addr, &obp_tftp_args.si6addr.addr, 16);
563         }
564         if (rc == -1) {
565                 strcpy(buf,"E3001: (net) Could not get IP address");
566                 bootmsg_error(0x3001, &buf[7]);
567
568                 write_mm_log(buf, strlen(buf), 0x91);
569                 return -101;
570         }
571
572         if (ip_version == 4) {
573                 printf("  Using IPv4 address: %d.%d.%d.%d\n",
574                         ((fn_ip.own_ip >> 24) & 0xFF), ((fn_ip.own_ip >> 16) & 0xFF),
575                         ((fn_ip.own_ip >>  8) & 0xFF), ( fn_ip.own_ip        & 0xFF));
576         } else if (ip_version == 6) {
577                 char ip6_str[40];
578                 ipv6_to_str(fn_ip.own_ip6.addr, ip6_str);
579                 printf("  Using IPv6 address: %s\n", ip6_str);
580         }
581
582         if (rc == -2) {
583                 sprintf(buf,
584                         "E3002: (net) ARP request to TFTP server "
585                         "(%d.%d.%d.%d) failed",
586                         ((fn_ip.server_ip >> 24) & 0xFF),
587                         ((fn_ip.server_ip >> 16) & 0xFF),
588                         ((fn_ip.server_ip >>  8) & 0xFF),
589                         ( fn_ip.server_ip        & 0xFF));
590                 bootmsg_error(0x3002, &buf[7]);
591
592                 write_mm_log(buf, strlen(buf), 0x91);
593                 return -102;
594         }
595         if (rc == -4 || rc == -3) {
596                 strcpy(buf,"E3008: (net) Can't obtain TFTP server IP address");
597                 bootmsg_error(0x3008, &buf[7]);
598
599                 write_mm_log(buf, strlen(buf), 0x91);
600                 return -107;
601         }
602
603
604         /***********************************************************
605          *
606          * Load file via TFTP into buffer provided by OpenFirmware
607          *
608          ***********************************************************/
609
610         if (obp_tftp_args.filename[0] != 0) {
611                 strncpy((char *) fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1);
612                 fn_ip.filename[sizeof(fn_ip.filename)-1] = 0;
613         }
614
615         if (ip_version == 4) {
616                 printf("  Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n",
617                         fn_ip.filename,
618                         ((fn_ip.server_ip >> 24) & 0xFF),
619                         ((fn_ip.server_ip >> 16) & 0xFF),
620                         ((fn_ip.server_ip >>  8) & 0xFF),
621                         ( fn_ip.server_ip        & 0xFF));
622         } else if (ip_version == 6) {
623                 char ip6_str[40];
624                 printf("  Requesting file \"%s\" via TFTP from ", fn_ip.filename);
625                 ipv6_to_str(fn_ip.server_ip6.addr, ip6_str);
626                 printf("%s\n", ip6_str);
627         }
628
629         // accept at most 20 bad packets
630         // wait at most for 40 packets
631         rc = tftp(&fn_ip, (unsigned char *) buffer,
632                   len, obp_tftp_args.tftp_retries,
633                   &tftp_err, huge_load, block_size, ip_version);
634
635         if(obp_tftp_args.ip_init == IP_INIT_DHCP)
636                 dhcp_send_release(fn_ip.fd);
637
638         if (rc > 0) {
639                 printf("  TFTP: Received %s (%d KBytes)\n", fn_ip.filename,
640                        rc / 1024);
641         } else if (rc == -1) {
642                 bootmsg_error(0x3003, "(net) unknown TFTP error");
643                 return -103;
644         } else if (rc == -2) {
645                 sprintf(buf,
646                         "E3004: (net) TFTP buffer of %d bytes "
647                         "is too small for %s",
648                         len, fn_ip.filename);
649                 bootmsg_error(0x3004, &buf[7]);
650
651                 write_mm_log(buf, strlen(buf), 0x91);
652                 return -104;
653         } else if (rc == -3) {
654                 sprintf(buf,"E3009: (net) file not found: %s",
655                        fn_ip.filename);
656                 bootmsg_error(0x3009, &buf[7]);
657
658                 write_mm_log(buf, strlen(buf), 0x91);
659                 return -108;
660         } else if (rc == -4) {
661                 strcpy(buf,"E3010: (net) TFTP access violation");
662                 bootmsg_error(0x3010, &buf[7]);
663
664                 write_mm_log(buf, strlen(buf), 0x91);
665                 return -109;
666         } else if (rc == -5) {
667                 strcpy(buf,"E3011: (net) illegal TFTP operation");
668                 bootmsg_error(0x3011, &buf[7]);
669
670                 write_mm_log(buf, strlen(buf), 0x91);
671                 return -110;
672         } else if (rc == -6) {
673                 strcpy(buf, "E3012: (net) unknown TFTP transfer ID");
674                 bootmsg_error(0x3012, &buf[7]);
675
676                 write_mm_log(buf, strlen(buf), 0x91);
677                 return -111;
678         } else if (rc == -7) {
679                 strcpy(buf, "E3013: (net) no such TFTP user");
680                 bootmsg_error(0x3013, &buf[7]);
681
682                 write_mm_log(buf, strlen(buf), 0x91);
683                 return -112;
684         } else if (rc == -8) {
685                 strcpy(buf, "E3017: (net) TFTP blocksize negotiation failed");
686                 bootmsg_error(0x3017, &buf[7]);
687
688                 write_mm_log(buf, strlen(buf), 0x91);
689                 return -116;
690         } else if (rc == -9) {
691                 strcpy(buf,"E3018: (net) file exceeds maximum TFTP transfer size");
692                 bootmsg_error(0x3018, &buf[7]);
693
694                 write_mm_log(buf, strlen(buf), 0x91);
695                 return -117;
696         } else if (rc <= -10 && rc >= -15) {
697                 sprintf(buf,"E3005: (net) ICMP ERROR \"");
698                 switch (rc) {
699                 case -ICMP_NET_UNREACHABLE - 10:
700                         sprintf(buf+strlen(buf),"net unreachable");
701                         break;
702                 case -ICMP_HOST_UNREACHABLE - 10:
703                         sprintf(buf+strlen(buf),"host unreachable");
704                         break;
705                 case -ICMP_PROTOCOL_UNREACHABLE - 10:
706                         sprintf(buf+strlen(buf),"protocol unreachable");
707                         break;
708                 case -ICMP_PORT_UNREACHABLE - 10:
709                         sprintf(buf+strlen(buf),"port unreachable");
710                         break;
711                 case -ICMP_FRAGMENTATION_NEEDED - 10:
712                         sprintf(buf+strlen(buf),"fragmentation needed and DF set");
713                         break;
714                 case -ICMP_SOURCE_ROUTE_FAILED - 10:
715                         sprintf(buf+strlen(buf),"source route failed");
716                         break;
717                 default:
718                         sprintf(buf+strlen(buf)," UNKNOWN");
719                         break;
720                 }
721                 sprintf(buf+strlen(buf),"\"");
722                 bootmsg_error(0x3005, &buf[7]);
723
724                 write_mm_log(buf, strlen(buf), 0x91);
725                 return -105;
726         } else if (rc == -40) {
727                 sprintf(buf,
728                         "E3014: (net) TFTP error occurred after "
729                         "%d bad packets received",
730                         tftp_err.bad_tftp_packets);
731                 bootmsg_error(0x3014, &buf[7]);
732                 write_mm_log(buf, strlen(buf), 0x91);
733                 return -113;
734         } else if (rc == -41) {
735                 sprintf(buf,
736                         "E3015: (net) TFTP error occurred after "
737                         "missing %d responses",
738                         tftp_err.no_packets);
739                 bootmsg_error(0x3015, &buf[7]);
740                 write_mm_log(buf, strlen(buf), 0x91);
741                 return -114;
742         } else if (rc == -42) {
743                 sprintf(buf,
744                         "E3016: (net) TFTP error missing block %d, "
745                         "expected block was %d",
746                         tftp_err.blocks_missed,
747                         tftp_err.blocks_received);
748                 bootmsg_error(0x3016, &buf[7]);
749                 write_mm_log(buf, strlen(buf), 0x91);
750                 return -115;
751         }
752         return rc;
753 }
754
755 /**
756  * Parses a tftp arguments, extracts all
757  * parameters and fills server ip according to this
758  *
759  * Parameters:
760  * @param  buffer        string with arguments,
761  * @param  server_ip     server ip as result
762  * @param  filename      default filename
763  * @param  fd            Socket descriptor
764  * @param  len           len of the buffer,
765  * @return               0 on SUCCESS and -1 on failure
766  */
767 int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd,
768                     int len)
769 {
770         char *raw;
771         char *tmp, *tmp1;
772         int i, j = 0;
773         char domainname[256];
774         uint8_t server_ip6[16];
775
776         raw = malloc(len);
777         if (raw == NULL) {
778                 printf("\n unable to allocate memory, parsing failed\n");
779                 return -1;
780         }
781         strncpy(raw,(const char *)buffer,len);
782         /*tftp url contains tftp://[fd00:4f53:4444:90:214:5eff:fed9:b200]/testfile*/
783         if(strncmp(raw,"tftp://",7)){
784                 printf("\n tftp missing in %s\n",raw);
785                 free(raw);
786                 return -1;
787         }
788         tmp = strchr(raw,'[');
789         if(tmp != NULL && *tmp == '[') {
790                 /*check for valid ipv6 address*/
791                 tmp1 = strchr(tmp,']');
792                 if (tmp1 == NULL) {
793                         printf("\n missing ] in %s\n",raw);
794                         free(raw);
795                         return -1;
796                 }
797                 i = tmp1 - tmp;
798                 /*look for file name*/
799                 tmp1 = strchr(tmp,'/');
800                 if (tmp1 == NULL) {
801                         printf("\n missing filename in %s\n",raw);
802                         free(raw);
803                         return -1;
804                 }
805                 tmp[i] = '\0';
806                 /*check for 16 byte ipv6 address */
807                 if (!str_to_ipv6((tmp+1), (uint8_t *)(server_ip))) {
808                         printf("\n wrong format IPV6 address in %s\n",raw);
809                         free(raw);
810                         return -1;;
811                 }
812                 else {
813                         /*found filename */
814                         strcpy(filename,(tmp1+1));
815                         free(raw);
816                         return 0;
817                 }
818         }
819         else {
820                 /*here tftp://hostname/testfile from option request of dhcp*/
821                 /*look for dns server name */
822                 tmp1 = strchr(raw,'.');
823                 if(tmp1 == NULL) {
824                         printf("\n missing . seperator in %s\n",raw);
825                         free(raw);
826                         return -1;
827                 }
828                 /*look for domain name beyond dns server name
829                 * so ignore the current . and look for one more
830                 */
831                 tmp = strchr((tmp1+1),'.');
832                 if(tmp == NULL) {
833                         printf("\n missing domain in %s\n",raw);
834                         free(raw);
835                         return -1;
836                 }
837                 tmp1 = strchr(tmp1,'/');
838                 if (tmp1 == NULL) {
839                         printf("\n missing filename in %s\n",raw);
840                         free(raw);
841                         return -1;
842                 }
843                 j = tmp1 - (raw + 7);
844                 tmp = raw + 7;
845                 tmp[j] = '\0';
846                 strcpy(domainname, tmp);
847                 if (dns_get_ip(fd, domainname, server_ip6, 6) == 0) {
848                         printf("\n DNS failed for IPV6\n");
849                         return -1;
850                 }
851                 ipv6_to_str(server_ip6, server_ip);
852
853                 strcpy(filename,(tmp1+1));
854                 free(raw);
855                 return 0;
856         }
857
858 }