aboutsummaryrefslogtreecommitdiff
path: root/lib/libnet
diff options
context:
space:
mode:
authorThomas Huth <thuth@redhat.com>2016-06-10 10:33:31 +0200
committerAlexey Kardashevskiy <aik@ozlabs.ru>2016-06-27 12:31:06 +1000
commite7ac2e5f658831ca686badb8530b0160b827d800 (patch)
tree8a6cdbb86cae3515fbe530b07623e0af5d9ad0ba /lib/libnet
parent3c1fe653f55c68b6e19733fc373bdf424feab0b3 (diff)
downloadSLOF-e7ac2e5f658831ca686badb8530b0160b827d800.zip
SLOF-e7ac2e5f658831ca686badb8530b0160b827d800.tar.gz
SLOF-e7ac2e5f658831ca686badb8530b0160b827d800.tar.bz2
net: Move also files from clients/net-snk/app/netapps/ to lib/libnet/
These files should go into libnet, too, so we can later link them to paflof instead of net-snk. Note: A "make distclean" is required after applying this patch to make sure that the dependencies for the moved files are properly re-generated. Signed-off-by: Thomas Huth <thuth@redhat.com> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Diffstat (limited to 'lib/libnet')
-rw-r--r--lib/libnet/Makefile2
-rw-r--r--lib/libnet/args.c179
-rw-r--r--lib/libnet/args.h23
-rw-r--r--lib/libnet/netapps.h29
-rw-r--r--lib/libnet/netload.c858
-rw-r--r--lib/libnet/ping.c215
6 files changed, 1305 insertions, 1 deletions
diff --git a/lib/libnet/Makefile b/lib/libnet/Makefile
index 14c29d3..2c9b7c2 100644
--- a/lib/libnet/Makefile
+++ b/lib/libnet/Makefile
@@ -20,7 +20,7 @@ CFLAGS += -I. -I.. -I../libc/include
CFLAGS += -I../../clients/net-snk/app/netapps -I../../clients/net-snk/include
SRCS = ethernet.c ipv4.c udp.c tcp.c dns.c bootp.c dhcp.c tftp.c \
- ipv6.c dhcpv6.c icmpv6.c ndp.c
+ ipv6.c dhcpv6.c icmpv6.c ndp.c netload.c ping.c args.c
OBJS = $(SRCS:%.c=%.o)
diff --git a/lib/libnet/args.c b/lib/libnet/args.c
new file mode 100644
index 0000000..3f057c3
--- /dev/null
+++ b/lib/libnet/args.c
@@ -0,0 +1,179 @@
+/******************************************************************************
+ * 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 <stdint.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include "args.h"
+
+/**
+ * Returns pointer of the n'th argument within a string.
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @param index index of the requested arguments within arg_str
+ * @return pointer of argument[index] on success
+ * NULL if index is out of range
+ */
+const char *
+get_arg_ptr(const char *arg_str, unsigned int index)
+{
+ unsigned int i;
+
+ for (i = 0; i < index; ++i) {
+ for (; *arg_str != ',' && *arg_str != 0; ++arg_str);
+ if (*arg_str == 0)
+ return 0;
+ ++arg_str;
+ }
+ return arg_str;
+}
+
+/**
+ * Returns number of arguments within a string.
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @return number of arguments
+ */
+unsigned int
+get_args_count(const char *arg_str)
+{
+ unsigned int count = 1;
+
+ while ((arg_str = get_arg_ptr(arg_str, 1)) != 0)
+ ++count;
+ return count;
+}
+
+/**
+ * Returns the length of the first argument.
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @return length of first argument
+ */
+unsigned int
+get_arg_length(const char *arg_str)
+{
+ unsigned int i;
+
+ for (i = 0; *arg_str != ',' && *arg_str != 0; ++i)
+ ++arg_str;
+ return i;
+}
+
+/**
+ * Copy the n'th argument within a string into a buffer in respect
+ * to a limited buffer size
+ *
+ * @param arg_str string with arguments, separated with ','
+ * @param index index of the requested arguments within arg_str
+ * @param buffer pointer to the buffer
+ * @param length size of the buffer
+ * @return pointer of buffer on success
+ * NULL if index is out of range.
+ */
+char *
+argncpy(const char *arg_str, unsigned int index, char *buffer,
+ unsigned int length)
+{
+ const char *ptr = get_arg_ptr(arg_str, index);
+ unsigned int len;
+
+ if (!ptr)
+ return 0;
+ len = get_arg_length(ptr);
+ if (!strncpy(buffer, ptr, length))
+ return 0;
+ buffer[len] = 0;
+ return buffer;
+}
+
+/**
+ * Converts "255.255.255.255\nn" -> char[4] = { 0xff, 0xff, 0xff, 0xff }
+ * *netmask = subnet_netmask(nn)
+ *
+ * @param str string to be converted
+ * @param ip in case of SUCCESS - 32-bit long IP
+ * in case of FAULT - zero
+ * @param netmask return netmask if there is a valid /nn encoding in IP
+ * @return TRUE - IP converted successfully;
+ * FALSE - error condition occurs (e.g. bad format)
+ */
+int
+strtoip_netmask(const char *str, char ip[4], unsigned int *netmask)
+{
+ char octet[10];
+ int res;
+ unsigned int i = 0, len, has_nn = 0;
+
+ while (*str != 0) {
+ if (i > 3 || !isdigit(*str))
+ return 0;
+ if (strstr(str, ".") != NULL) {
+ len = (int16_t) (strstr(str, ".") - str);
+ if (len >= 10)
+ return 0;
+ strncpy(octet, str, len);
+ octet[len] = 0;
+ str += len;
+ } else if (strchr(str, '\\') != NULL) {
+ len = (short) (strchr(str, '\\') - str);
+ if (len >= 10)
+ return 0;
+ strncpy(octet, str, len);
+ octet[len] = 0;
+ str += len;
+ has_nn = 1;
+ } else {
+ strncpy(octet, str, 9);
+ octet[9] = 0;
+ str += strlen(octet);
+ }
+ res = strtol(octet, NULL, 10);
+ if ((res > 255) || (res < 0))
+ return 0;
+ ip[i] = (char) res;
+ i++;
+ if (*str == '.')
+ str++;
+ if(has_nn) {
+ str++;
+ strncpy(octet, str, 9);
+ octet[9] = 0;
+ res = strtol(octet, NULL, 10);
+ str += strlen(octet);
+ if (res > 31 || res < 1)
+ return 0;
+ if (netmask)
+ *netmask = 0xFFFFFFFF << (32 - res);
+ }
+ }
+
+ if (i != 4)
+ return 0;
+ return -1;
+}
+
+/**
+ * Converts "255.255.255.255" -> char[4] = { 0xff, 0xff, 0xff, 0xff }
+ *
+ * @param str string to be converted
+ * @param ip in case of SUCCESS - 32-bit long IP
+ * in case of FAULT - zero
+ * @return TRUE - IP converted successfully;
+ * FALSE - error condition occurs (e.g. bad format)
+ */
+int
+strtoip(const char *str, char ip[4])
+{
+ return strtoip_netmask(str, ip, NULL);
+}
diff --git a/lib/libnet/args.h b/lib/libnet/args.h
new file mode 100644
index 0000000..1ede9a8
--- /dev/null
+++ b/lib/libnet/args.h
@@ -0,0 +1,23 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+#ifndef _ARGS_H
+#define _ARGS_H
+
+const char *get_arg_ptr(const char *, unsigned int);
+unsigned int get_args_count(const char *);
+unsigned int get_arg_length(const char *);
+char *argncpy(const char *, unsigned int, char *, unsigned int);
+int strtoip(const char *, char[4]);
+int strtoip_netmask(const char *, char[4], unsigned int *netmask);
+
+#endif /* _ARGS_H */
diff --git a/lib/libnet/netapps.h b/lib/libnet/netapps.h
new file mode 100644
index 0000000..d3f7eb2
--- /dev/null
+++ b/lib/libnet/netapps.h
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+#ifndef _NETAPPS_H_
+#define _NETAPPS_H_
+
+#include <tftp.h>
+
+#define F_IPV4 4
+#define F_IPV6 6
+
+int netboot(int argc, char *argv[]);
+int netsave(int argc, char *argv[]);
+int bcmflash(int argc, char *argv[]);
+int mac_sync(int argc, char *argv[]);
+int net_eeprom_version( void );
+int ping(int argc, char *argv[]);
+int dhcp(char *ret_buffer, filename_ip_t * fn_ip, unsigned int retries, int flags);
+
+#endif
diff --git a/lib/libnet/netload.c b/lib/libnet/netload.c
new file mode 100644
index 0000000..d99aa29
--- /dev/null
+++ b/lib/libnet/netload.c
@@ -0,0 +1,858 @@
+/******************************************************************************
+ * 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 <tftp.h>
+#include <ethernet.h>
+#include <dhcp.h>
+#include <dhcpv6.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <dns.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <libbootmsg/libbootmsg.h>
+#include <of.h>
+#include "args.h"
+#include "netapps.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 DEFAULT_BOOT_RETRIES 10
+#define DEFAULT_TFTP_RETRIES 20
+static int ip_version = 4;
+
+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;
+
+
+/**
+ * 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, filename_ip_t * 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);
+}
+
+int
+netboot(int argc, char *argv[])
+{
+ char buf[256];
+ int rc;
+ int len = strtol(argv[2], 0, 16);
+ char *buffer = (char *) strtol(argv[1], 0, 16);
+ char *ret_buffer = (char *) strtol(argv[3], 0, 16);
+ filename_ip_t fn_ip;
+ int fd_device;
+ tftp_err_t tftp_err;
+ 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 };
+ int huge_load = strtol(argv[4], 0, 10);
+ int32_t block_size = strtol(argv[5], 0, 10);
+ uint8_t own_mac[6];
+
+ 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(0, 0, 0, (char*) own_mac);
+ if(fd_device != -2)
+ break;
+ if(getchar() == 27) {
+ fd_device = -2;
+ break;
+ }
+ }
+
+ if (fd_device == -1) {
+ strcpy(buf,"E3000: (net) Could not read MAC address");
+ bootmsg_error(0x3000, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -100;
+ }
+ else if (fd_device == -2) {
+ strcpy(buf,"E3006: (net) Could not initialize network device");
+ bootmsg_error(0x3006, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -101;
+ }
+
+ 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 (argc > 6) {
+ parse_args(argv[6], &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(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries);
+ break;
+ case IP_INIT_DHCP:
+ rc = dhcp(ret_buffer, &fn_ip, obp_tftp_args.bootp_retries, F_IPV4);
+ break;
+ case IP_INIT_DHCPV6_STATELESS:
+ rc = dhcp(ret_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(ret_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) {
+ strcpy(buf,"E3001: (net) Could not get IP address");
+ bootmsg_error(0x3001, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -101;
+ }
+
+ 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) {
+ sprintf(buf,
+ "E3002: (net) 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));
+ bootmsg_error(0x3002, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -102;
+ }
+ if (rc == -4 || rc == -3) {
+ strcpy(buf,"E3008: (net) Can't obtain TFTP server IP address");
+ bootmsg_error(0x3008, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -107;
+ }
+
+
+ /***********************************************************
+ *
+ * Load file via TFTP into buffer provided by OpenFirmware
+ *
+ ***********************************************************/
+
+ if (obp_tftp_args.filename[0] != 0) {
+ strncpy((char *) fn_ip.filename, obp_tftp_args.filename, sizeof(fn_ip.filename)-1);
+ fn_ip.filename[sizeof(fn_ip.filename)-1] = 0;
+ }
+
+ 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);
+ }
+
+ // accept at most 20 bad packets
+ // wait at most for 40 packets
+ rc = tftp(&fn_ip, (unsigned char *) buffer,
+ len, obp_tftp_args.tftp_retries,
+ &tftp_err, huge_load, block_size, ip_version);
+
+ if(obp_tftp_args.ip_init == IP_INIT_DHCP)
+ dhcp_send_release(fn_ip.fd);
+
+ if (rc > 0) {
+ printf(" TFTP: Received %s (%d KBytes)\n", fn_ip.filename,
+ rc / 1024);
+ } else if (rc == -1) {
+ bootmsg_error(0x3003, "(net) unknown TFTP error");
+ return -103;
+ } else if (rc == -2) {
+ sprintf(buf,
+ "E3004: (net) TFTP buffer of %d bytes "
+ "is too small for %s",
+ len, fn_ip.filename);
+ bootmsg_error(0x3004, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -104;
+ } else if (rc == -3) {
+ sprintf(buf,"E3009: (net) file not found: %s",
+ fn_ip.filename);
+ bootmsg_error(0x3009, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -108;
+ } else if (rc == -4) {
+ strcpy(buf,"E3010: (net) TFTP access violation");
+ bootmsg_error(0x3010, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -109;
+ } else if (rc == -5) {
+ strcpy(buf,"E3011: (net) illegal TFTP operation");
+ bootmsg_error(0x3011, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -110;
+ } else if (rc == -6) {
+ strcpy(buf, "E3012: (net) unknown TFTP transfer ID");
+ bootmsg_error(0x3012, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -111;
+ } else if (rc == -7) {
+ strcpy(buf, "E3013: (net) no such TFTP user");
+ bootmsg_error(0x3013, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -112;
+ } else if (rc == -8) {
+ strcpy(buf, "E3017: (net) TFTP blocksize negotiation failed");
+ bootmsg_error(0x3017, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -116;
+ } else if (rc == -9) {
+ strcpy(buf,"E3018: (net) file exceeds maximum TFTP transfer size");
+ bootmsg_error(0x3018, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -117;
+ } else if (rc <= -10 && rc >= -15) {
+ sprintf(buf,"E3005: (net) ICMP ERROR \"");
+ switch (rc) {
+ case -ICMP_NET_UNREACHABLE - 10:
+ sprintf(buf+strlen(buf),"net unreachable");
+ break;
+ case -ICMP_HOST_UNREACHABLE - 10:
+ sprintf(buf+strlen(buf),"host unreachable");
+ break;
+ case -ICMP_PROTOCOL_UNREACHABLE - 10:
+ sprintf(buf+strlen(buf),"protocol unreachable");
+ break;
+ case -ICMP_PORT_UNREACHABLE - 10:
+ sprintf(buf+strlen(buf),"port unreachable");
+ break;
+ case -ICMP_FRAGMENTATION_NEEDED - 10:
+ sprintf(buf+strlen(buf),"fragmentation needed and DF set");
+ break;
+ case -ICMP_SOURCE_ROUTE_FAILED - 10:
+ sprintf(buf+strlen(buf),"source route failed");
+ break;
+ default:
+ sprintf(buf+strlen(buf)," UNKNOWN");
+ break;
+ }
+ sprintf(buf+strlen(buf),"\"");
+ bootmsg_error(0x3005, &buf[7]);
+
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -105;
+ } else if (rc == -40) {
+ sprintf(buf,
+ "E3014: (net) TFTP error occurred after "
+ "%d bad packets received",
+ tftp_err.bad_tftp_packets);
+ bootmsg_error(0x3014, &buf[7]);
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -113;
+ } else if (rc == -41) {
+ sprintf(buf,
+ "E3015: (net) TFTP error occurred after "
+ "missing %d responses",
+ tftp_err.no_packets);
+ bootmsg_error(0x3015, &buf[7]);
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -114;
+ } else if (rc == -42) {
+ sprintf(buf,
+ "E3016: (net) TFTP error missing block %d, "
+ "expected block was %d",
+ tftp_err.blocks_missed,
+ tftp_err.blocks_received);
+ bootmsg_error(0x3016, &buf[7]);
+ write_mm_log(buf, strlen(buf), 0x91);
+ return -115;
+ }
+ return rc;
+}
+
+/**
+ * Parses a tftp arguments, extracts all
+ * parameters and fills server ip according to this
+ *
+ * Parameters:
+ * @param buffer string with arguments,
+ * @param server_ip server ip as result
+ * @param filename default filename
+ * @param fd Socket descriptor
+ * @param len len of the buffer,
+ * @return 0 on SUCCESS and -1 on failure
+ */
+int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd,
+ int len)
+{
+ char *raw;
+ char *tmp, *tmp1;
+ int i, j = 0;
+ char domainname[256];
+ uint8_t server_ip6[16];
+
+ raw = malloc(len);
+ if (raw == NULL) {
+ printf("\n unable to allocate memory, parsing failed\n");
+ return -1;
+ }
+ strncpy(raw,(const char *)buffer,len);
+ /*tftp url contains tftp://[fd00:4f53:4444:90:214:5eff:fed9:b200]/testfile*/
+ if(strncmp(raw,"tftp://",7)){
+ printf("\n tftp missing in %s\n",raw);
+ free(raw);
+ return -1;
+ }
+ tmp = strchr(raw,'[');
+ if(tmp != NULL && *tmp == '[') {
+ /*check for valid ipv6 address*/
+ tmp1 = strchr(tmp,']');
+ if (tmp1 == NULL) {
+ printf("\n missing ] in %s\n",raw);
+ free(raw);
+ return -1;
+ }
+ i = tmp1 - tmp;
+ /*look for file name*/
+ tmp1 = strchr(tmp,'/');
+ if (tmp1 == NULL) {
+ printf("\n missing filename in %s\n",raw);
+ free(raw);
+ return -1;
+ }
+ tmp[i] = '\0';
+ /*check for 16 byte ipv6 address */
+ if (!str_to_ipv6((tmp+1), (uint8_t *)(server_ip))) {
+ printf("\n wrong format IPV6 address in %s\n",raw);
+ free(raw);
+ return -1;;
+ }
+ else {
+ /*found filename */
+ strcpy(filename,(tmp1+1));
+ free(raw);
+ return 0;
+ }
+ }
+ else {
+ /*here tftp://hostname/testfile from option request of dhcp*/
+ /*look for dns server name */
+ tmp1 = strchr(raw,'.');
+ if(tmp1 == NULL) {
+ printf("\n missing . seperator in %s\n",raw);
+ free(raw);
+ return -1;
+ }
+ /*look for domain name beyond dns server name
+ * so ignore the current . and look for one more
+ */
+ tmp = strchr((tmp1+1),'.');
+ if(tmp == NULL) {
+ printf("\n missing domain in %s\n",raw);
+ free(raw);
+ return -1;
+ }
+ tmp1 = strchr(tmp1,'/');
+ if (tmp1 == NULL) {
+ printf("\n missing filename in %s\n",raw);
+ free(raw);
+ return -1;
+ }
+ j = tmp1 - (raw + 7);
+ tmp = raw + 7;
+ tmp[j] = '\0';
+ strcpy(domainname, tmp);
+ if (dns_get_ip(fd, domainname, server_ip6, 6) == 0) {
+ printf("\n DNS failed for IPV6\n");
+ return -1;
+ }
+ ipv6_to_str(server_ip6, server_ip);
+
+ strcpy(filename,(tmp1+1));
+ free(raw);
+ return 0;
+ }
+
+}
diff --git a/lib/libnet/ping.c b/lib/libnet/ping.c
new file mode 100644
index 0000000..def3179
--- /dev/null
+++ b/lib/libnet/ping.c
@@ -0,0 +1,215 @@
+/******************************************************************************
+ * 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 <ipv4.h>
+#include <dhcp.h>
+#include <ethernet.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "args.h"
+#include "netapps.h"
+
+struct ping_args {
+ union {
+ char string[4];
+ unsigned int integer;
+ } server_ip;
+ union {
+ char string[4];
+ unsigned int integer;
+ } client_ip;
+ union {
+ char string[4];
+ unsigned int integer;
+ } gateway_ip;
+ unsigned int timeout;
+ unsigned int netmask;
+};
+
+static void
+usage(void)
+{
+ printf
+ ("\nping device-path:[device-args,]server-ip,[client-ip[\\nn]],[gateway-ip][,timeout]\n");
+
+}
+
+static int
+parse_args(const char *args, struct ping_args *ping_args)
+{
+ unsigned int argc = get_args_count(args);
+ char buf[64];
+ ping_args->timeout = 10;
+ if (argc == 0)
+ /* at least server-ip has to be specified */
+ return -1;
+ if (argc == 1) {
+ /* probably only server ip is specified */
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->server_ip.string))
+ return -1;
+ return 0;
+ }
+ /* get first option from list */
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->server_ip.string)) {
+ /* it is not an IP address
+ * therefore it has to be device-args
+ * device-args are not supported and just ignored */
+ args = get_arg_ptr(args, 1);
+ argc--;
+ }
+
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->server_ip.string)) {
+ /* this should have been the server IP address */
+ return -1;
+ } else {
+ args = get_arg_ptr(args, 1);
+ if (!--argc)
+ return 0;
+ }
+
+ argncpy(args, 0, buf, 64);
+ if (!strtoip_netmask(buf, ping_args->client_ip.string, &ping_args->netmask)) {
+ /* this should have been the client (our) IP address */
+ return -1;
+ } else {
+ args = get_arg_ptr(args, 1);
+ if (!--argc)
+ return 0;
+ }
+ argncpy(args, 0, buf, 64);
+ if (!strtoip(buf, ping_args->gateway_ip.string)) {
+ /* this should have been the gateway IP address */
+ return -1;
+ } else {
+ args = get_arg_ptr(args, 1);
+ if (!--argc)
+ return 0;
+ }
+ argncpy(args, 0, buf, 64);
+ ping_args->timeout = strtol(args, 0, 10);
+ return 0;
+}
+
+int
+ping(int argc, char *argv[])
+{
+ short arp_failed = 0;
+ filename_ip_t fn_ip;
+ int fd_device;
+ struct ping_args ping_args;
+ uint8_t own_mac[6];
+ uint32_t netmask;
+
+ memset(&ping_args, 0, sizeof(struct ping_args));
+
+ if (argc == 2) {
+ if (parse_args(argv[1], &ping_args)) {
+ usage();
+ return -1;
+ }
+ } else {
+ usage();
+ return -1;
+ }
+
+ memset(&fn_ip, 0, sizeof(filename_ip_t));
+
+ /* Get mac_addr from device */
+ printf("\n Reading MAC address from device: ");
+ fd_device = socket(0, 0, 0, (char *) own_mac);
+ if (fd_device == -1) {
+ printf("\nE3000: Could not read MAC address\n");
+ return -100;
+ } else if (fd_device == -2) {
+ printf("\nE3006: Could not initialize network device\n");
+ return -101;
+ }
+
+ fn_ip.fd = fd_device;
+
+ printf("%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);
+ // identify the BOOTP/DHCP server via broadcasts
+ // don't do this, when using DHCP !!!
+ // fn_ip.server_ip = 0xFFFFFFFF;
+ // memset(fn_ip.server_mac, 0xff, 6);
+
+ if (!ping_args.client_ip.integer) {
+ /* Get ip address for our mac address */
+ printf(" Requesting IP address via DHCP: ");
+ arp_failed = dhcp(0, &fn_ip, 30, F_IPV4);
+
+ if (arp_failed == -1) {
+ printf("\n DHCP: Could not get ip address\n");
+ return -1;
+ }
+
+ } else {
+ memcpy(&fn_ip.own_ip, &ping_args.client_ip.integer, 4);
+ if (ping_args.gateway_ip.integer)
+ set_ipv4_router(ping_args.gateway_ip.integer);
+ if (!ping_args.netmask) {
+ /* Netmask is not provided, assume default according to
+ * the network class
+ */
+ ping_args.netmask = get_default_ipv4_netmask(ping_args.client_ip.string);
+ }
+ set_ipv4_netmask(ping_args.netmask);
+
+ arp_failed = 1;
+ printf(" Own IP address: ");
+ }
+
+ // reinit network stack
+ set_ipv4_address(fn_ip.own_ip);
+
+ printf("%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));
+
+ netmask = get_ipv4_netmask();
+ if (netmask) {
+ printf(" Netmask : ");
+ printf("%d.%d.%d.%d\n", ((netmask >> 24) & 0xFF), ((netmask >> 16) & 0xFF),
+ ((netmask >> 8) & 0xFF), (netmask & 0xFF));
+ }
+
+ memcpy(&fn_ip.server_ip, &ping_args.server_ip.integer, 4);
+ printf(" Ping to %d.%d.%d.%d ", ((fn_ip.server_ip >> 24) & 0xFF),
+ ((fn_ip.server_ip >> 16) & 0xFF),
+ ((fn_ip.server_ip >> 8) & 0xFF), (fn_ip.server_ip & 0xFF));
+
+
+ ping_ipv4(fd_device, fn_ip.server_ip);
+
+ set_timer(TICKS_SEC / 10 * ping_args.timeout);
+ while(get_timer() > 0) {
+ receive_ether(fd_device);
+ if(pong_ipv4() == 0) {
+ printf("success\n");
+ return 0;
+ }
+ }
+
+ printf("failed\n");
+ return -1;
+}