qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC PATCH 05/14] pc-bios/s390-ccw: Add the TFTP network lo


From: Thomas Huth
Subject: [Qemu-devel] [RFC PATCH 05/14] pc-bios/s390-ccw: Add the TFTP network loading stack from SLOF
Date: Tue, 27 Jun 2017 13:48:11 +0200

Add the files for TFTP network loading from SLOF to the s390-ccw
firmware. The files have been copied unmodified from SLOF commit
ID 62674aabe20612a9786fa03e87cf6916ba97a99a an will be adjusted
for the s390-ccw firmware by the following commits.

Signed-off-by: Thomas Huth <address@hidden>
---
 pc-bios/s390-ccw/libnet/Makefile   |  50 ++
 pc-bios/s390-ccw/libnet/args.c     | 179 +++++++
 pc-bios/s390-ccw/libnet/args.h     |  23 +
 pc-bios/s390-ccw/libnet/dhcp.c     | 955 +++++++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/dhcp.h     |  49 ++
 pc-bios/s390-ccw/libnet/dhcpv6.c   | 212 ++++++++
 pc-bios/s390-ccw/libnet/dhcpv6.h   | 154 ++++++
 pc-bios/s390-ccw/libnet/dns.c      | 526 ++++++++++++++++++++
 pc-bios/s390-ccw/libnet/dns.h      |  28 ++
 pc-bios/s390-ccw/libnet/ethernet.c | 189 ++++++++
 pc-bios/s390-ccw/libnet/ethernet.h |  47 ++
 pc-bios/s390-ccw/libnet/icmpv6.c   | 409 ++++++++++++++++
 pc-bios/s390-ccw/libnet/icmpv6.h   | 135 ++++++
 pc-bios/s390-ccw/libnet/ipv4.c     | 898 ++++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/ipv4.h     |  97 ++++
 pc-bios/s390-ccw/libnet/ipv6.c     | 774 ++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/ipv6.h     | 188 ++++++++
 pc-bios/s390-ccw/libnet/ndp.c      | 184 +++++++
 pc-bios/s390-ccw/libnet/ndp.h      |  72 +++
 pc-bios/s390-ccw/libnet/netapps.h  |  28 ++
 pc-bios/s390-ccw/libnet/netload.c  | 868 +++++++++++++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/tcp.c      |  46 ++
 pc-bios/s390-ccw/libnet/tcp.h      |  27 ++
 pc-bios/s390-ccw/libnet/tftp.c     | 594 +++++++++++++++++++++++
 pc-bios/s390-ccw/libnet/tftp.h     |  52 ++
 pc-bios/s390-ccw/libnet/time.h     |   6 +
 pc-bios/s390-ccw/libnet/udp.c      | 115 +++++
 pc-bios/s390-ccw/libnet/udp.h      |  53 ++
 28 files changed, 6958 insertions(+)
 create mode 100644 pc-bios/s390-ccw/libnet/Makefile
 create mode 100644 pc-bios/s390-ccw/libnet/args.c
 create mode 100644 pc-bios/s390-ccw/libnet/args.h
 create mode 100644 pc-bios/s390-ccw/libnet/dhcp.c
 create mode 100644 pc-bios/s390-ccw/libnet/dhcp.h
 create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/dhcpv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/dns.c
 create mode 100644 pc-bios/s390-ccw/libnet/dns.h
 create mode 100644 pc-bios/s390-ccw/libnet/ethernet.c
 create mode 100644 pc-bios/s390-ccw/libnet/ethernet.h
 create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/icmpv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/ipv4.c
 create mode 100644 pc-bios/s390-ccw/libnet/ipv4.h
 create mode 100644 pc-bios/s390-ccw/libnet/ipv6.c
 create mode 100644 pc-bios/s390-ccw/libnet/ipv6.h
 create mode 100644 pc-bios/s390-ccw/libnet/ndp.c
 create mode 100644 pc-bios/s390-ccw/libnet/ndp.h
 create mode 100644 pc-bios/s390-ccw/libnet/netapps.h
 create mode 100644 pc-bios/s390-ccw/libnet/netload.c
 create mode 100644 pc-bios/s390-ccw/libnet/tcp.c
 create mode 100644 pc-bios/s390-ccw/libnet/tcp.h
 create mode 100644 pc-bios/s390-ccw/libnet/tftp.c
 create mode 100644 pc-bios/s390-ccw/libnet/tftp.h
 create mode 100644 pc-bios/s390-ccw/libnet/time.h
 create mode 100644 pc-bios/s390-ccw/libnet/udp.c
 create mode 100644 pc-bios/s390-ccw/libnet/udp.h

diff --git a/pc-bios/s390-ccw/libnet/Makefile b/pc-bios/s390-ccw/libnet/Makefile
new file mode 100644
index 0000000..83ac1e5
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/Makefile
@@ -0,0 +1,50 @@
+# *****************************************************************************
+# * 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 TOP
+  TOP = $(shell while ! test -e make.rules; do cd ..  ; done; pwd)
+  export TOP
+endif
+include $(TOP)/make.rules
+
+CFLAGS += -I. -I.. -I../libc/include -I$(TOP)/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 netload.c ping.c args.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+TARGET = ../libnet.a
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+       $(AR) -rc $@ $(OBJS)
+       $(RANLIB) $@
+
+clean:
+       $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+       $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+       $(RM) Makefile.dep
+       $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+       $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
diff --git a/pc-bios/s390-ccw/libnet/args.c b/pc-bios/s390-ccw/libnet/args.c
new file mode 100644
index 0000000..3f057c3
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/args.h b/pc-bios/s390-ccw/libnet/args.h
new file mode 100644
index 0000000..1ede9a8
--- /dev/null
+++ b/pc-bios/s390-ccw/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/pc-bios/s390-ccw/libnet/dhcp.c b/pc-bios/s390-ccw/libnet/dhcp.c
new file mode 100644
index 0000000..0cb4fa4
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dhcp.c
@@ -0,0 +1,955 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+
+/******************************* ALGORITHMS ******************************/
+
+/** \file dhcp.c <pre>
+ * **************** State-transition diagram for DHCP client  *************
+ *
+ *   +---------+                  Note: DHCP-server msg / DHCP-client msg
+ *   |  INIT   |
+ *   +---------+
+ *        |
+ *        |  - / Discover
+ *        V
+ *   +---------+
+ *   | SELECT  |                     Timeout
+ *   +---------+                        |
+ *        |                             |
+ *        |  Offer / Request            |
+ *        |                             |
+ *        V                             V
+ *   +---------+     NACK / -      ***********
+ *   | REQUEST | ----------------> *  FAULT  *
+ *   +---------+                   ***********
+ *        |
+ *        |          ACK / -       ***********
+ *        +----------------------> * SUCCESS *
+ *                                 ***********
+ *
+ * ************************************************************************
+ * </pre> */
+
+
+/********************** DEFINITIONS & DECLARATIONS ***********************/
+
+#include <dhcp.h>
+#include <ethernet.h>
+#include <ipv4.h>
+#include <udp.h>
+#include <dns.h>
+#include <args.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+/* DHCP Message Types */
+#define DHCPDISCOVER    1
+#define DHCPOFFER       2
+#define DHCPREQUEST     3
+#define DHCPDECLINE     4
+#define DHCPACK                5
+#define DHCPNACK        6
+#define DHCPRELEASE     7
+#define DHCPINFORM      8
+
+/* DHCP Option Codes */
+#define DHCP_MASK              1
+#define DHCP_ROUTER            3
+#define DHCP_DNS               6
+#define DHCP_REQUESTED_IP     50
+#define DHCP_OVERLOAD         52
+#define DHCP_MSG_TYPE         53
+#define DHCP_SERVER_ID        54
+#define DHCP_REQUEST_LIST     55
+#define DHCP_TFTP_SERVER      66
+#define DHCP_BOOTFILE         67
+#define DHCP_CLIENT_ARCH      93
+#define DHCP_ENDOPT         0xFF
+#define DHCP_PADOPT         0x00
+
+/* "file/sname" overload option values */
+#define DHCP_OVERLOAD_FILE     1
+#define DHCP_OVERLOAD_SNAME    2
+#define DHCP_OVERLOAD_BOTH     3
+
+/* DHCP states codes */
+#define DHCP_STATE_SELECT      1
+#define DHCP_STATE_REQUEST     2
+#define DHCP_STATE_SUCCESS     3
+#define DHCP_STATE_FAULT       4
+
+/* DHCP Client Architecture */
+#ifndef DHCPARCH
+#define USE_DHCPARCH 0
+#define DHCPARCH 0
+#else
+#define USE_DHCPARCH 1
+#endif
+
+static uint8_t dhcp_magic[] = {0x63, 0x82, 0x53, 0x63};
+/**< DHCP_magic is a cookie, that identifies DHCP options (see RFC 2132) */
+
+/** \struct dhcp_options_t
+ *  This structure is used to fill options in DHCP-msg during transmitting
+ *  or to retrieve options from DHCP-msg during receiving.
+ *  <p>
+ *  If flag[i] == TRUE then field for i-th option retains valid value and
+ *  information from this field may retrived (in case of receiving) or will
+ *  be transmitted (in case of transmitting).
+ *
+ */
+typedef struct {
+       uint8_t    flag[256];         /**< Show if corresponding opt. is valid 
*/
+       uint8_t    request_list[256]; /**< o.55 If i-th member is TRUE, then 
i-th
+                                         option will be requested from server 
*/
+       uint32_t   server_ID;         /**< o.54 Identifies DHCP-server         
*/
+       uint32_t   requested_IP;      /**< o.50 Must be filled in DHCP-Request 
*/
+       uint32_t   dns_IP;            /**< o. 6 DNS IP                         
*/
+       uint32_t   router_IP;         /**< o. 3 Router IP                      
*/
+       uint32_t   subnet_mask;       /**< o. 1 Subnet mask                    
*/
+       uint8_t    msg_type;          /**< o.53 DHCP-message type              
*/
+       uint8_t    overload;          /**< o.52 Overload sname/file fields     
*/
+       int8_t     tftp_server[256];  /**< o.66 TFTP server name               
*/
+       int8_t     bootfile[256];     /**< o.67 Boot file name                 
*/
+       uint16_t   client_arch;       /**< o.93 Client architecture type       
*/
+} dhcp_options_t;
+
+/** Stores state of DHCP-client (refer to State-transition diagram) */
+static uint8_t dhcp_state;
+
+
+/***************************** PROTOTYPES ********************************/
+
+static int32_t dhcp_attempt(int fd);
+
+static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * 
opt_struct);
+
+static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
+                                  dhcp_options_t * opt_struct);
+
+static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
+                                uint8_t src_options[], uint32_t src_len);
+
+static int8_t dhcp_find_option(uint8_t options[], uint32_t len,
+                              uint8_t op_code, uint32_t * op_offset);
+
+static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
+                              uint8_t * new_option);
+
+static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
+                               uint32_t dst_offset, uint8_t * new_option);
+
+static void dhcp_send_discover(int fd);
+
+static void dhcp_send_request(int fd);
+
+/***************************** LOCAL VARIABLES ***************************/
+
+static uint8_t  ether_packet[ETH_MTU_SIZE];
+static uint32_t dhcp_own_ip        = 0;
+static uint32_t dhcp_server_ip     = 0;
+static uint32_t dhcp_siaddr_ip     = 0;
+static char   dhcp_filename[256];
+static char   dhcp_tftp_name[256];
+static uint32_t dhcp_xid;
+
+static char   * response_buffer;
+
+/***************************** IMPLEMENTATION ****************************/
+
+void dhcpv4_generate_transaction_id(void)
+{
+       dhcp_xid = (rand() << 16) ^ rand();
+}
+
+int32_t dhcpv4(char *ret_buffer, filename_ip_t *fn_ip)
+{
+       uint32_t dhcp_tftp_ip     = 0;
+       int fd = fn_ip->fd;
+
+       strcpy(dhcp_filename, "");
+       strcpy(dhcp_tftp_name, "");
+
+       response_buffer = ret_buffer;
+
+       if (dhcp_attempt(fd) == 0)
+               return -1;
+
+       if (fn_ip->own_ip) {
+               dhcp_own_ip = fn_ip->own_ip;
+       }
+       if (fn_ip->server_ip) {
+               dhcp_siaddr_ip = fn_ip->server_ip;
+       }
+       if(fn_ip->filename[0] != 0) {
+               strcpy(dhcp_filename, (char *) fn_ip->filename);
+       }
+
+       // TFTP SERVER
+       if (!strlen(dhcp_tftp_name)) {
+               if (!dhcp_siaddr_ip) {
+                       // ERROR: TFTP name is not presented
+                       return -3;
+               }
+
+               // take TFTP-ip from siaddr field
+               dhcp_tftp_ip = dhcp_siaddr_ip;
+       }
+       else {
+               // TFTP server defined by its name
+               if (!strtoip(dhcp_tftp_name, (char *)&dhcp_tftp_ip)) {
+                       if (!dns_get_ip(fd, dhcp_tftp_name, (uint8_t 
*)&dhcp_tftp_ip, 4)) {
+                               // DNS error - can't obtain TFTP-server name
+                               // Use TFTP-ip from siaddr field, if presented
+                               if (dhcp_siaddr_ip) {
+                                       dhcp_tftp_ip = dhcp_siaddr_ip;
+                               }
+                               else {
+                                       // ERROR: Can't obtain TFTP server IP
+                                       return -4;
+                               }
+                       }
+               }
+       }
+
+       // Store configuration info into filename_ip strucutre
+       fn_ip -> own_ip = dhcp_own_ip;
+       fn_ip -> server_ip = dhcp_tftp_ip;
+       strcpy((char *) fn_ip -> filename, dhcp_filename);
+
+       return 0;
+}
+
+/**
+ * DHCP: Tries o obtain DHCP parameters, refer to state-transition diagram
+ */
+static int32_t dhcp_attempt(int fd)
+{
+       int sec;
+
+       // Send DISCOVER message and switch DHCP-client to SELECT state
+       dhcp_send_discover(fd);
+
+       dhcp_state = DHCP_STATE_SELECT;
+
+       // setting up a timer with a timeout of two seconds
+       for (sec = 0; sec < 2; sec++) {
+               set_timer(TICKS_SEC);
+               do {
+                       receive_ether(fd);
+
+                       // Wait until client will switch to Final state or 
Timeout occurs
+                       switch (dhcp_state) {
+                       case DHCP_STATE_SUCCESS :
+                               return 1;
+                       case DHCP_STATE_FAULT :
+                               return 0;
+                       }
+               } while (get_timer() > 0);
+       }
+
+       // timeout
+       return 0;
+}
+
+/**
+ * DHCP: Supplements DHCP-message with options stored in structure.
+ *       For more information about option coding see dhcp_options_t.
+ *
+ * @param  opt_field     Points to the "vend" field of DHCP-message
+ *                       (destination)
+ * @param  opt_struct    this structure stores info about the options which
+ *                       will be added to DHCP-message (source)
+ * @return               TRUE - options packed;
+ *                       FALSE - error condition occurs.
+ * @see                  dhcp_options_t
+ */
+static int32_t dhcp_encode_options(uint8_t * opt_field, dhcp_options_t * 
opt_struct)
+{
+       uint8_t * options = opt_field;
+       uint16_t i, sum; // used to define is any options set
+
+       // magic
+       memcpy(options, dhcp_magic, 4);
+       options += 4;
+
+       // fill message type
+       switch (opt_struct -> msg_type) {
+       case DHCPDISCOVER :
+       case DHCPREQUEST :
+       case DHCPDECLINE :
+       case DHCPINFORM :
+       case DHCPRELEASE :
+               options[0] = DHCP_MSG_TYPE;
+               options[1] = 1;
+               options[2] = opt_struct -> msg_type;
+               options += 3;
+               break;
+       default :
+               return 0; // Unsupported DHCP-message
+       }
+
+       if (opt_struct -> overload) {
+               options[0] = DHCP_OVERLOAD;
+               options[1] = 0x01;
+               options[2] = opt_struct -> overload;
+               options +=3;
+       }
+
+       if (opt_struct -> flag[DHCP_REQUESTED_IP]) {
+               options[0] = DHCP_REQUESTED_IP;
+               options[1] = 0x04;
+               * (uint32_t *) (options + 2) = htonl (opt_struct -> 
requested_IP);
+               options +=6;
+       }
+
+       if (opt_struct -> flag[DHCP_SERVER_ID]) {
+               options[0] = DHCP_SERVER_ID;
+               options[1] = 0x04;
+               * (uint32_t *) (options + 2) = htonl (opt_struct -> server_ID);
+               options +=6;
+       }
+
+       sum = 0;
+       for (i = 0; i < 256; i++)
+               sum += opt_struct -> request_list[i];
+
+       if (sum) {
+               options[0] = DHCP_REQUEST_LIST;
+               options[1] = sum;
+               options += 2;
+               for (i = 0; i < 256; i++) {
+                       if (opt_struct -> request_list[i]) {
+                               options[0] = i; options++;
+                       }
+               }
+       }
+
+       if (opt_struct -> flag[DHCP_TFTP_SERVER]) {
+               options[0] = DHCP_TFTP_SERVER;
+               options[1] = strlen((char *) opt_struct -> tftp_server) + 1;
+               memcpy(options + 2, opt_struct -> tftp_server, options[1]);
+               options += options[1] + 2;
+       }
+
+       if (opt_struct -> flag[DHCP_BOOTFILE]) {
+               options[0] = DHCP_BOOTFILE;
+               options[1] = strlen((char *) opt_struct -> bootfile) + 1;
+               memcpy(options + 2, opt_struct -> bootfile, options[1]);
+               options += options[1] + 2;
+       }
+
+       if (opt_struct -> flag[DHCP_CLIENT_ARCH]) {
+               options[0] = DHCP_CLIENT_ARCH;
+               options[1] = 2;
+               options[2] = (DHCPARCH >> 8);
+               options[3] = DHCPARCH & 0xff;
+               options += 4;
+       }
+
+       // end options
+       options[0] = 0xFF;
+       options++;
+
+       return 1;
+}
+
+/**
+ * DHCP: Extracts encoded options from DHCP-message into the structure.
+ *       For more information about option coding see dhcp_options_t.
+ *
+ * @param  opt_field     Points to the "options" field of DHCP-message
+ *                       (source).
+ * @param  opt_len       Length of "options" field.
+ * @param  opt_struct    this structure stores info about the options which
+ *                       was extracted from DHCP-message (destination).
+ * @return               TRUE - options extracted;
+ *                       FALSE - error condition occurs.
+ * @see                  dhcp_options_t
+ */
+static int32_t dhcp_decode_options(uint8_t opt_field[], uint32_t opt_len,
+                                  dhcp_options_t * opt_struct)
+{
+       uint32_t offset = 0;
+
+       memset(opt_struct, 0, sizeof(dhcp_options_t));
+
+       // magic
+       if (memcmp(opt_field, dhcp_magic, 4)) {
+               return 0;
+       }
+
+       offset += 4;
+       while (offset < opt_len) {
+               opt_struct -> flag[opt_field[offset]] = 1;
+               switch(opt_field[offset]) {
+               case DHCP_OVERLOAD :
+                       opt_struct -> overload = opt_field[offset + 2];
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_REQUESTED_IP :
+                       opt_struct -> requested_IP = htonl(* (uint32_t *) 
(opt_field + offset + 2));
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_MASK :
+                       opt_struct -> flag[DHCP_MASK] = 1;
+                       opt_struct -> subnet_mask = htonl(* (uint32_t *) 
(opt_field + offset + 2));
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_DNS :
+                       opt_struct -> flag[DHCP_DNS] = 1;
+                       opt_struct -> dns_IP = htonl(* (uint32_t *) (opt_field 
+ offset + 2));
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_ROUTER :
+                       opt_struct -> flag[DHCP_ROUTER] = 1;
+                       opt_struct -> router_IP = htonl(* (uint32_t *) 
(opt_field + offset + 2));
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_MSG_TYPE :
+                       if ((opt_field[offset + 2] > 0) && (opt_field[offset + 
2] < 9))
+                               opt_struct -> msg_type = opt_field[offset + 2];
+                       else
+                               return 0;
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_SERVER_ID :
+                       opt_struct -> server_ID = htonl(* (uint32_t *) 
(opt_field + offset + 2));
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_TFTP_SERVER   :
+                       memcpy(opt_struct -> tftp_server, opt_field + offset + 
2, opt_field[offset + 1]);
+                       (opt_struct -> tftp_server)[opt_field[offset + 1]] = 0;
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_BOOTFILE :
+                       memcpy(opt_struct ->  bootfile, opt_field + offset + 2, 
opt_field[offset + 1]);
+                       (opt_struct -> bootfile)[opt_field[offset + 1]] = 0;
+                       offset += 2 + opt_field[offset + 1];
+                       break;
+
+               case DHCP_CLIENT_ARCH :
+                       opt_struct -> client_arch = ((opt_field[offset + 2] << 
8) & 0xFF00) | (opt_field[offset + 3] & 0xFF);
+                       offset += 4;
+                       break;
+
+               case DHCP_PADOPT :
+                       offset++;
+                       break;
+
+               case DHCP_ENDOPT :  // End of options
+                       return 1;
+
+               default :
+                       offset += 2 + opt_field[offset + 1]; // Unsupported 
opt. - do nothing
+               }
+       }
+       if (offset == opt_len)
+               return 1; // options finished without 0xFF
+
+       return 0;
+}
+
+/**
+ * DHCP: Appends information from source "options" into dest "options".
+ *       This function is used to support "file/sname" overloading.
+ *
+ * @param  dst_options   destanation "options" field
+ * @param  dst_len       size of dst_options (modified by this function)
+ * @param  src_options   source "options" field
+ * @param  src_len       size of src_options
+ * @return               TRUE - options merged;
+ *                       FALSE - error condition occurs.
+ */
+static int8_t dhcp_merge_options(uint8_t dst_options[], uint32_t * dst_len,
+                                uint8_t src_options[], uint32_t src_len)
+{
+       uint32_t dst_offset, src_offset = 0;
+
+       // remove ENDOPT if presented
+       if (dhcp_find_option(dst_options, * dst_len, DHCP_ENDOPT, &dst_offset))
+               * dst_len = dst_offset;
+
+       while (src_offset < src_len) {
+               switch(src_options[src_offset]) {
+               case DHCP_PADOPT:
+                       src_offset++;
+                       break;
+               case DHCP_ENDOPT:
+                       return 1;
+               default:
+                       if (dhcp_find_option(dst_options, * dst_len,
+                                            src_options[src_offset],
+                                            &dst_offset)) {
+                               dhcp_combine_option(dst_options, dst_len,
+                                                   dst_offset,
+                                                   (uint8_t *) src_options +
+                                                   src_offset);
+                       }
+                       else {
+                               dhcp_append_option(dst_options, dst_len, 
src_options + src_offset);
+                       }
+                       src_offset += 2 + src_options[src_offset + 1];
+               }
+       }
+
+       if (src_offset == src_len)
+               return 1;
+       return 0;
+}
+
+/**
+ * DHCP: Finds given occurrence of the option with the given code (op_code)
+ *       in "options" field of DHCP-message.
+ *
+ * @param  options       "options" field of DHCP-message
+ * @param  len           length of the "options" field
+ * @param  op_code       code of the option to find
+ * @param  op_offset     SUCCESS - offset to an option occurrence;
+ *                       FAULT - offset is set to zero.
+ * @return               TRUE - option was find;
+ *                       FALSE - option wasn't find.
+ */
+static int8_t dhcp_find_option(uint8_t options[], uint32_t len,
+                              uint8_t op_code, uint32_t * op_offset)
+{
+       uint32_t srch_offset = 0;
+       * op_offset = 0;
+
+       while (srch_offset < len) {
+               if (options[srch_offset] == op_code) {
+                       * op_offset = srch_offset;
+                       return 1;
+               }
+               if (options[srch_offset] == DHCP_ENDOPT)
+                       return 0;
+
+               if (options[srch_offset] == DHCP_PADOPT)
+                       srch_offset++;
+               else
+                       srch_offset += 2 + options[srch_offset + 1];
+       }
+       return 0;
+}
+
+/**
+ * DHCP: Appends new option from one list (src) into the tail
+ *       of another option list (dst)
+ *
+ * @param  dst_options   "options" field of DHCP-message
+ * @param  dst_len       length of the "options" field (modified)
+ * @param  new_option    points to an option in another list (src)
+ */
+static void dhcp_append_option(uint8_t dst_options[], uint32_t * dst_len,
+                              uint8_t * new_option)
+{
+       memcpy(dst_options + ( * dst_len), new_option, 2 + (* (new_option + 
1)));
+       * dst_len += 2 + *(new_option + 1);
+}
+
+/**
+ * DHCP: This function is used when options with the same code are
+ *       presented in both merged lists. In this case information
+ *       about the option from one list (src) is combined (complemented)
+ *       with information about the option in another list (dst).
+ *
+ * @param  dst_options  "options" field of DHCP-message
+ * @param  dst_len       length of the "options" field (modified)
+ * @param  dst_offset    offset of the option from beginning of the list
+ * @param  new_option    points to an option in another list (src)
+ */
+static void dhcp_combine_option(uint8_t dst_options[], uint32_t * dst_len,
+                               uint32_t dst_offset, uint8_t * new_option)
+{
+       uint8_t tmp_buffer[1024]; // use to provide safe memcpy
+       uint32_t tail_len;
+
+       // move all subsequent options (allocate size for additional info)
+       tail_len = (* dst_len) - dst_offset - 2 - dst_options[dst_offset + 1];
+
+       memcpy(tmp_buffer, dst_options + (* dst_len) - tail_len, tail_len);
+       memcpy(dst_options + (* dst_len) - tail_len + (* (new_option + 1)),
+              tmp_buffer, tail_len);
+
+       // add new_content to option
+       memcpy(dst_options + (* dst_len) - tail_len, new_option + 2,
+              * (new_option + 1));
+       dst_options[dst_offset + 1] += * (new_option + 1);
+
+       // correct dst_len
+       * dst_len += * (new_option + 1);
+}
+
+/**
+ * DHCP: Sends DHCP-Discover message. Looks for DHCP servers.
+ */
+static void dhcp_send_discover(int fd)
+{
+       uint32_t packetsize = sizeof(struct iphdr) +
+                             sizeof(struct udphdr) + sizeof(struct btphdr);
+       struct btphdr *btph;
+       dhcp_options_t opt;
+
+       memset(ether_packet, 0, packetsize);
+
+       btph = (struct btphdr *) (&ether_packet[
+              sizeof(struct iphdr) + sizeof(struct udphdr)]);
+
+       btph -> op = 1;
+       btph -> htype = 1;
+       btph -> hlen = 6;
+       btph -> xid = dhcp_xid;
+       memcpy(btph -> chaddr, get_mac_address(), 6);
+
+       memset(&opt, 0, sizeof(dhcp_options_t));
+
+       opt.msg_type = DHCPDISCOVER;
+
+       opt.request_list[DHCP_MASK] = 1;
+       opt.request_list[DHCP_DNS] = 1;
+       opt.request_list[DHCP_ROUTER] = 1;
+       opt.request_list[DHCP_TFTP_SERVER] = 1;
+       opt.request_list[DHCP_BOOTFILE] = 1;
+       opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
+
+       dhcp_encode_options(btph -> vend, &opt);
+
+       fill_udphdr(&ether_packet[sizeof(struct iphdr)],
+                   sizeof(struct btphdr) + sizeof(struct udphdr),
+                   UDPPORT_BOOTPC, UDPPORT_BOOTPS);
+       fill_iphdr(ether_packet, sizeof(struct btphdr) +
+                  sizeof(struct udphdr) + sizeof(struct iphdr),
+                  IPTYPE_UDP, dhcp_own_ip, 0xFFFFFFFF);
+
+       send_ipv4(fd, ether_packet, packetsize);
+}
+
+/**
+ * DHCP: Sends DHCP-Request message. Asks for acknowledgment to occupy IP.
+ */
+static void dhcp_send_request(int fd)
+{
+       uint32_t packetsize = sizeof(struct iphdr) +
+                             sizeof(struct udphdr) + sizeof(struct btphdr);
+       struct btphdr *btph;
+       dhcp_options_t opt;
+
+       memset(ether_packet, 0, packetsize);
+
+       btph = (struct btphdr *) (&ether_packet[
+              sizeof(struct iphdr) + sizeof(struct udphdr)]);
+
+       btph -> op = 1;
+       btph -> htype = 1;
+       btph -> hlen = 6;
+       btph -> xid = dhcp_xid;
+       memcpy(btph -> chaddr, get_mac_address(), 6);
+
+       memset(&opt, 0, sizeof(dhcp_options_t));
+
+       opt.msg_type = DHCPREQUEST;
+       memcpy(&(opt.requested_IP), &dhcp_own_ip, 4);
+       opt.flag[DHCP_REQUESTED_IP] = 1;
+       memcpy(&(opt.server_ID), &dhcp_server_ip, 4);
+       opt.flag[DHCP_SERVER_ID] = 1;
+
+       opt.request_list[DHCP_MASK] = 1;
+       opt.request_list[DHCP_DNS] = 1;
+       opt.request_list[DHCP_ROUTER] = 1;
+       opt.request_list[DHCP_TFTP_SERVER] = 1;
+       opt.request_list[DHCP_BOOTFILE] = 1;
+       opt.request_list[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
+       opt.flag[DHCP_CLIENT_ARCH] = USE_DHCPARCH;
+
+       dhcp_encode_options(btph -> vend, &opt);
+
+       fill_udphdr(&ether_packet[sizeof(struct iphdr)],
+                   sizeof(struct btphdr) + sizeof(struct udphdr),
+                   UDPPORT_BOOTPC, UDPPORT_BOOTPS);
+       fill_iphdr(ether_packet, sizeof(struct btphdr) +
+                  sizeof(struct udphdr) + sizeof(struct iphdr),
+                  IPTYPE_UDP, 0, 0xFFFFFFFF);
+
+       send_ipv4(fd, ether_packet, packetsize);
+}
+
+
+/**
+ * DHCP: Sends DHCP-Release message. Releases occupied IP.
+ */
+void dhcp_send_release(int fd)
+{
+       uint32_t packetsize = sizeof(struct iphdr) +
+                             sizeof(struct udphdr) + sizeof(struct btphdr);
+       struct btphdr *btph;
+       dhcp_options_t opt;
+
+       btph = (struct btphdr *) (&ether_packet[
+              sizeof(struct iphdr) + sizeof(struct udphdr)]);
+
+       memset(ether_packet, 0, packetsize);
+
+       btph -> op = 1;
+       btph -> htype = 1;
+       btph -> hlen = 6;
+       btph -> xid = dhcp_xid;
+       strcpy((char *) btph -> file, "");
+       memcpy(btph -> chaddr, get_mac_address(), 6);
+       btph -> ciaddr = htonl(dhcp_own_ip);
+
+       memset(&opt, 0, sizeof(dhcp_options_t));
+
+       opt.msg_type = DHCPRELEASE;
+       opt.server_ID = dhcp_server_ip;
+       opt.flag[DHCP_SERVER_ID] = 1;
+
+       dhcp_encode_options(btph -> vend, &opt);
+
+       fill_udphdr(&ether_packet[sizeof(struct iphdr)],
+                   sizeof(struct btphdr) + sizeof(struct udphdr),
+                   UDPPORT_BOOTPC, UDPPORT_BOOTPS);
+       fill_iphdr(ether_packet, sizeof(struct btphdr) +
+                  sizeof(struct udphdr) + sizeof(struct iphdr), IPTYPE_UDP,
+                  dhcp_own_ip, dhcp_server_ip);
+
+       send_ipv4(fd, ether_packet, packetsize);
+}
+
+/**
+ * DHCP: Handles DHCP-messages according to Receive-handle diagram.
+ *       Changes the state of DHCP-client.
+ *
+ * @param  fd         socket descriptor
+ * @param  packet     BootP/DHCP-packet to be handled
+ * @param  packetsize length of the packet
+ * @return            ZERO - packet handled successfully;
+ *                    NON ZERO - packet was not handled (e.g. bad format)
+ * @see               receive_ether
+ * @see               btphdr
+ */
+
+int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize)
+{
+       struct btphdr * btph;
+       struct iphdr * iph;
+       dhcp_options_t opt;
+
+       memset(&opt, 0, sizeof(dhcp_options_t));
+       btph = (struct btphdr *) packet;
+       iph = (struct iphdr *) packet - sizeof(struct udphdr) -
+             sizeof(struct iphdr);
+
+       if (btph->op != 2)
+               return -1;              /* It is not a Bootp/DHCP reply */
+       if (btph->xid != dhcp_xid)
+               return -1;              /* The transaction ID does not match */
+
+       if (memcmp(btph -> vend, dhcp_magic, 4)) {
+               // It is BootP - RFC 951
+               dhcp_own_ip    = htonl(btph -> yiaddr);
+               dhcp_siaddr_ip = htonl(btph -> siaddr);
+               dhcp_server_ip = htonl(iph -> ip_src);
+
+               if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
+                       strncpy((char *) dhcp_tftp_name, (char *) btph -> sname,
+                               sizeof(btph -> sname));
+                       dhcp_tftp_name[sizeof(btph -> sname)] = 0;
+               }
+
+               if (strlen((char *) btph -> file)) {
+                       strncpy((char *) dhcp_filename, (char *) btph -> file, 
sizeof(btph -> file));
+                       dhcp_filename[sizeof(btph -> file)] = 0;
+               }
+
+               dhcp_state = DHCP_STATE_SUCCESS;
+               return 0;
+       }
+
+
+       // decode options
+       if (!dhcp_decode_options(btph -> vend, packetsize -
+                                sizeof(struct btphdr) + sizeof(btph -> vend),
+                                &opt)) {
+               return -1;  // can't decode options
+       }
+
+       if (opt.overload) {
+               int16_t decode_res = 0;
+               uint8_t options[1024]; // buffer for merged options
+               uint32_t opt_len;
+
+               // move 1-st part of options from vend field into buffer
+               opt_len = packetsize - sizeof(struct btphdr) +
+                         sizeof(btph -> vend) - 4;
+               memcpy(options, btph -> vend, opt_len + 4);
+
+               // add other parts
+               switch (opt.overload) {
+               case DHCP_OVERLOAD_FILE:
+                       decode_res = dhcp_merge_options(options + 4, &opt_len,
+                                                       btph -> file,
+                                                       sizeof(btph -> file));
+                       break;
+               case DHCP_OVERLOAD_SNAME:
+                       decode_res = dhcp_merge_options(options + 4, &opt_len,
+                                                       btph -> sname,
+                                                       sizeof(btph -> sname));
+                       break;
+               case DHCP_OVERLOAD_BOTH:
+                       decode_res = dhcp_merge_options(options + 4, &opt_len,
+                                                       btph -> file,
+                                                       sizeof(btph -> file));
+                       if (!decode_res)
+                               break;
+                       decode_res = dhcp_merge_options(options + 4, &opt_len,
+                                                       btph -> sname,
+                                                       sizeof(btph -> sname));
+                       break;
+               }
+
+               if (!decode_res)
+                       return -1; // bad options in sname/file fields
+
+               // decode merged options
+               if (!dhcp_decode_options(options, opt_len + 4, &opt)) {
+                       return -1; // can't decode options
+               }
+       }
+
+       if (!opt.msg_type) {
+               // It is BootP with Extensions - RFC 1497
+               // retrieve conf. settings from BootP - reply
+               dhcp_own_ip = htonl(btph -> yiaddr);
+               dhcp_siaddr_ip = htonl(btph -> siaddr);
+               if (strlen((char *) btph -> sname) && !dhcp_siaddr_ip) {
+                       strncpy((char *) dhcp_tftp_name, (char *) btph -> 
sname, sizeof(btph -> sname));
+                       dhcp_tftp_name[sizeof(btph -> sname)] = 0;
+               }
+
+               if (strlen((char *) btph -> file)) {
+                       strncpy((char *) dhcp_filename, (char *) btph -> file, 
sizeof(btph -> file));
+                       dhcp_filename[sizeof(btph -> file)] = 0;
+               }
+
+               // retrieve DHCP-server IP from IP-header
+               dhcp_server_ip = iph -> htonl(ip_src);
+
+               dhcp_state = DHCP_STATE_SUCCESS;
+       }
+       else {
+               // It is DHCP - RFC 2131 & RFC 2132
+               // opt contains parameters from server
+               switch (dhcp_state) {
+               case DHCP_STATE_SELECT :
+                       if (opt.msg_type == DHCPOFFER) {
+                               dhcp_own_ip = htonl(btph -> yiaddr);
+                               dhcp_server_ip = opt.server_ID;
+                               dhcp_send_request(fd);
+                               dhcp_state = DHCP_STATE_REQUEST;
+                       }
+                       return 0;
+               case DHCP_STATE_REQUEST :
+                       switch (opt.msg_type) {
+                       case DHCPNACK :
+                               dhcp_own_ip = 0;
+                               dhcp_server_ip = 0;
+                               dhcp_state = DHCP_STATE_FAULT;
+                               break;
+                       case DHCPACK :
+                               dhcp_own_ip = htonl(btph -> yiaddr);
+                               dhcp_server_ip = opt.server_ID;
+                               dhcp_siaddr_ip = htonl(btph -> siaddr);
+                               if (opt.flag[DHCP_TFTP_SERVER]) {
+                                       strcpy((char *) dhcp_tftp_name, (char 
*) opt.tftp_server);
+                               }
+                               else {
+                                       strcpy((char *) dhcp_tftp_name, "");
+                                       if ((opt.overload != 
DHCP_OVERLOAD_SNAME &&
+                                            opt.overload != 
DHCP_OVERLOAD_BOTH) &&
+                                            !dhcp_siaddr_ip) {
+                                               strncpy((char *) dhcp_tftp_name,
+                                                       (char *) btph->sname,
+                                                       sizeof(btph -> sname));
+                                               
dhcp_tftp_name[sizeof(btph->sname)] = 0;
+                                       }
+                               }
+
+                               if (opt.flag[DHCP_BOOTFILE]) {
+                                       strcpy((char *) dhcp_filename, (char *) 
opt.bootfile);
+                               }
+                               else {
+                                       strcpy((char *) dhcp_filename, "");
+                                       if (opt.overload != DHCP_OVERLOAD_FILE 
&&
+                                               opt.overload != 
DHCP_OVERLOAD_BOTH &&
+                                               strlen((char *) btph -> file)) {
+                                               strncpy((char *) dhcp_filename,
+                                                       (char *) btph->file,
+                                                       sizeof(btph->file));
+                                               dhcp_filename[sizeof(btph -> 
file)] = 0;
+                                       }
+                               }
+
+                               dhcp_state = DHCP_STATE_SUCCESS;
+                               break;
+                       default:
+                               break; // Unused DHCP-message - do nothing
+                       }
+                       break;
+               default :
+                       return -1; // Illegal DHCP-client state
+               }
+       }
+
+       if (dhcp_state == DHCP_STATE_SUCCESS) {
+
+               // initialize network entity with real own_ip
+               // to be able to answer for foreign requests
+               set_ipv4_address(dhcp_own_ip);
+
+               if(response_buffer) {
+                       if(packetsize <= 1720)
+                               memcpy(response_buffer, packet, packetsize);
+                       else
+                               memcpy(response_buffer, packet, 1720);
+               }
+
+               /* Subnet mask */
+               if (opt.flag[DHCP_MASK]) {
+                       /* Router */
+                       if (opt.flag[DHCP_ROUTER]) {
+                               set_ipv4_router(opt.router_IP);
+                               set_ipv4_netmask(opt.subnet_mask);
+                       }
+               }
+
+               /* DNS-server */
+               if (opt.flag[DHCP_DNS]) {
+                       dns_init(opt.dns_IP, 0, 4);
+               }
+       }
+
+       return 0;
+}
diff --git a/pc-bios/s390-ccw/libnet/dhcp.h b/pc-bios/s390-ccw/libnet/dhcp.h
new file mode 100644
index 0000000..4432c9b
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dhcp.h
@@ -0,0 +1,49 @@
+/******************************************************************************
+ * 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 _DHCP_H_
+#define _DHCP_H_
+
+#include <stdint.h>
+#include "tftp.h"
+
+/** \struct btphdr
+ *  A header for BootP/DHCP-messages.
+ *  For more information see RFC 951 / RFC 2131.
+ */
+struct btphdr {
+       uint8_t op;          /**< Identifies is it request (1) or reply (2)    
*/
+       uint8_t htype;       /**< HW address type (ethernet usually)           
*/
+       uint8_t hlen;        /**< HW address length                            
*/
+       uint8_t hops;        /**< This info used by relay agents (not used)    
*/
+       uint32_t xid;        /**< This ID is used to match queries and replies 
*/
+       uint16_t secs;       /**< Unused                                       
*/
+       uint16_t unused;     /**< Unused                                       
*/
+       uint32_t ciaddr;     /**< Client IP address (if client knows it)       
*/
+       uint32_t yiaddr;     /**< "Your" (client) IP address                   
*/
+       uint32_t siaddr;     /**< Next server IP address (TFTP server IP)      
*/
+       uint32_t giaddr;     /**< Gateway IP address (used by relay agents)    
*/
+       uint8_t chaddr[16];  /**< Client HW address                            
*/
+       uint8_t sname[64];   /**< Server host name (TFTP server name)          
*/
+       uint8_t file[128];   /**< Boot file name                               
*/
+       uint8_t vend[64];    /**< Optional parameters field (DHCP-options)     
*/
+};
+
+void dhcpv4_generate_transaction_id(void);
+int bootp(char *ret_buffer, filename_ip_t *, unsigned int);
+int dhcpv4(char *ret_buffer, filename_ip_t *);
+void dhcp_send_release(int fd);
+
+/* Handles DHCP-packets, which are detected by receive_ether. */
+extern int8_t handle_dhcp(int fd, uint8_t * packet, int32_t packetsize);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/dhcpv6.c b/pc-bios/s390-ccw/libnet/dhcpv6.c
new file mode 100644
index 0000000..491d540
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dhcpv6.c
@@ -0,0 +1,212 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <time.h>
+#include "ethernet.h"
+#include "ipv6.h"
+#include "udp.h"
+#include "dhcpv6.h"
+#include "tftp.h"
+#include "dns.h"
+
+static uint8_t tid[3];
+static uint32_t dhcpv6_state = -1;
+static filename_ip_t *my_fn_ip;
+
+static struct ip6addr_list_entry all_dhcpv6_ll; /* All DHCPv6 servers address 
*/
+
+void
+dhcpv6_generate_transaction_id(void)
+{
+       /* As per RFC 3315 transaction IDs should be generated randomly */
+       tid[0] = rand();
+       tid[1] = rand();
+       tid[2] = rand();
+}
+
+static void
+send_info_request(int fd)
+{
+       uint8_t ether_packet[ETH_MTU_SIZE];
+       uint32_t payload_length;
+       struct dhcp_message_header *dhcph;
+
+       memset(ether_packet, 0, ETH_MTU_SIZE);
+
+       /* Get an IPv6 packet */
+       payload_length = sizeof(struct udphdr) + sizeof(struct 
dhcp_message_header);
+       fill_ip6hdr (ether_packet + sizeof(struct ethhdr),
+                    payload_length, IPTYPE_UDP,
+                    get_ipv6_address(), &(all_dhcpv6_ll.addr));
+       fill_udphdr ( ether_packet + sizeof(struct ethhdr) + sizeof(struct 
ip6hdr),
+                     payload_length, DHCP_CLIENT_PORT, DHCP_SERVER_PORT);
+       dhcph = (struct dhcp_message_header *) (ether_packet +
+                                               sizeof(struct ethhdr) +
+                                               sizeof(struct ip6hdr) +
+                                               sizeof(struct udphdr));
+
+       /* Fill in DHCPv6 data */
+       dhcph->type = DHCP_INFORMATION_REQUEST;
+       memcpy( &(dhcph->transaction_id), &tid, 3);
+       dhcph->option.client_id.code = DHCPV6_OPTION_CLIENTID;
+       dhcph->option.client_id.length = 10;
+       dhcph->option.client_id.duid_type = DUID_LL;
+       dhcph->option.client_id.hardware_type = 1;
+       memcpy( &(dhcph->option.client_id.mac),
+               get_mac_address(), 6);
+       dhcph->option.el_time.code = DHCPV6_OPTION_ELAPSED_TIME;
+       dhcph->option.el_time.length = 2;
+       dhcph->option.el_time.time = 0x190; /* 4000 ms */
+       dhcph->option.option_request_option.code = DHCPV6_OPTION_ORO;
+       dhcph->option.option_request_option.length = DHCPV6_OPTREQUEST_NUMOPTS 
* 2;
+       dhcph->option.option_request_option.option_code[0] = 
DHCPV6_OPTION_DNS_SERVERS;
+       dhcph->option.option_request_option.option_code[1] = 
DHCPV6_OPTION_DOMAIN_LIST;
+       dhcph->option.option_request_option.option_code[2] = 
DHCPV6_OPTION_BOOT_URL;
+
+       send_ipv6(fd, ether_packet + sizeof(struct ethhdr),
+                 sizeof(struct ip6hdr) + sizeof(struct udphdr)
+                 + sizeof(struct dhcp_message_header));
+}
+
+static int32_t
+dhcpv6_attempt(int fd)
+{
+       int sec;
+
+       // Send information request
+       send_info_request(fd);
+
+       dhcpv6_state = DHCPV6_STATE_SELECT;
+
+       // setting up a timer with a timeout of two seconds
+       for (sec = 0; sec < 2; sec++) {
+               set_timer(TICKS_SEC);
+               do {
+                       receive_ether(fd);
+
+                       // Wait until client will switch to Final state or 
Timeout occurs
+                       switch (dhcpv6_state) {
+                       case DHCP_STATUSCODE_SUCCESS:
+                               return 1;
+                       case DHCP_STATUSCODE_UNSPECFAIL: //FIXME
+                               return 0;
+                       }
+               } while (get_timer() > 0);
+       }
+
+       // timeout
+       return 0;
+}
+
+int32_t
+dhcpv6 ( char *ret_buffer, void *fn_ip)
+{
+       int fd;
+
+       all_dhcpv6_ll.addr.part.prefix = 0xff02000000000000ULL;
+       all_dhcpv6_ll.addr.part.interface_id = 0x10002ULL;
+
+       my_fn_ip = (filename_ip_t *) fn_ip;
+       fd = my_fn_ip->fd;
+
+       if( !dhcpv6_attempt(fd)) {
+               return -1;
+       }
+
+       return 0;
+}
+
+static void dhcp6_process_options (uint8_t *option, int32_t option_length)
+{
+       struct dhcp_boot_url *option_boot_url;
+       struct client_identifier *option_clientid;
+       struct server_identifier *option_serverid;
+       struct dhcp_dns *option_dns;
+       struct dhcp_dns_list *option_dns_list;
+       struct dhcp6_gen_option *option_gen;
+       char buffer[256];
+
+       while (option_length > 0) {
+               switch ((uint16_t) *(option+1)) {
+               case DHCPV6_OPTION_CLIENTID:
+                       option_clientid = (struct client_identifier *) option;
+                       option = option +  option_clientid->length + 4;
+                       option_length = option_length - option_clientid->length 
- 4;
+                       break;
+               case DHCPV6_OPTION_SERVERID:
+                       option_serverid = (struct server_identifier *) option;
+                       option = option +  option_serverid->length + 4;
+                       option_length = option_length - option_serverid->length 
- 4;
+                       break;
+               case DHCPV6_OPTION_DNS_SERVERS:
+                       option_dns = (struct dhcp_dns *) option;
+                       option = option +  option_dns->length + 4;
+                       option_length = option_length - option_dns->length - 4;
+                       memcpy( &(my_fn_ip->dns_ip6),
+                               option_dns->p_ip6,
+                               IPV6_ADDR_LENGTH);
+                       dns_init(0, option_dns->p_ip6, 6);
+                       break;
+               case DHCPV6_OPTION_DOMAIN_LIST:
+                       option_dns_list = (struct dhcp_dns_list *) option;
+                       option = option +  option_dns_list->length + 4;
+                       option_length = option_length - option_dns_list->length 
- 4;
+                       break;
+               case DHCPV6_OPTION_BOOT_URL:
+                       option_boot_url = (struct dhcp_boot_url *) option;
+                       option = option +  option_boot_url->length + 4;
+                       option_length = option_length - option_boot_url->length 
- 4;
+                       strncpy((char *)buffer,
+                               (const char *)option_boot_url->url,
+                               (size_t)option_boot_url->length);
+                       buffer[option_boot_url->length] = 0;
+                       if (parse_tftp_args(buffer,
+                                           (char *)my_fn_ip->server_ip6.addr,
+                                           (char *)my_fn_ip->filename,
+                                           (int)my_fn_ip->fd,
+                                           option_boot_url->length) == -1)
+                               return;
+                       break;
+               default:
+                       option_gen = (struct dhcp6_gen_option *) option;
+                       option = option + option_gen->length + 4;
+                       option_length = option_length - option_gen->length - 4;
+               }
+       }
+}
+
+uint32_t
+handle_dhcpv6(uint8_t * packet, int32_t packetsize)
+{
+
+       uint8_t  *first_option;
+       int32_t option_length;
+       struct dhcp_message_reply *reply;
+       reply = (struct dhcp_message_reply *) packet;
+
+       if (memcmp(reply->transaction_id, tid, 3))
+               return -1;                      /* Wrong transaction ID */
+
+       if (reply->type == 7)
+               dhcpv6_state = DHCP_STATUSCODE_SUCCESS;
+
+       first_option =  packet + 4;
+       option_length =  packet + packetsize - first_option;
+       dhcp6_process_options(first_option, option_length);
+
+       return 0;
+}
diff --git a/pc-bios/s390-ccw/libnet/dhcpv6.h b/pc-bios/s390-ccw/libnet/dhcpv6.h
new file mode 100644
index 0000000..02747a0
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dhcpv6.h
@@ -0,0 +1,154 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 _DHCPV6_H_
+#define _DHCPV6_H_
+
+#include <stdint.h>
+#include "ethernet.h"
+
+#define DHCPV6_STATELESS 0
+#define DHCPV6_STATEFUL  1
+
+/* DHCP port numbers */
+#define DHCP_CLIENT_PORT       546
+#define DHCP_SERVER_PORT       547
+
+/* DHCPv6 message types         */
+#define DHCP_SOLICIT             1
+#define DHCP_ADVERTISE           2
+#define DHCP_REQUEST             3
+#define DHCP_CONFIRM             4
+#define DHCP_RENEW               5
+#define DHCP_REBIND              6
+#define DHCP_REPLY               7
+#define DHCP_RELEASE             8
+#define DHCP_DECLINE             9
+#define DHCP_RECONFIGURE        10
+#define DHCP_INFORMATION_REQUEST 11
+#define RELAY_FORW              12
+#define RELAY_REPL              13
+
+/* DHCPv6 option types */
+#define DHCPV6_OPTION_CLIENTID 0x0001
+#define DHCPV6_OPTION_SERVERID 0x0002
+#define DHCPV6_OPTION_IA_NA    3
+#define DHCPV6_OPTION_IA_TA    4
+#define DHCPV6_OPTION_IAADDR   5
+#define DHCPV6_OPTION_ORO      6
+#define DHCPV6_OPTION_PREFEREN 7
+#define DHCPV6_OPTION_ELAPSED_TIME     8
+#define DHCPV6_OPTION_RELAY_MS 9
+#define DHCPV6_OPTION_AUTH     11
+#define DHCPV6_OPTION_UNICAST  12
+#define DHCPV6_OPTION_STATUS_C 13
+#define DHCPV6_OPTION_RAPID_CO 14
+#define DHCPV6_OPTION_USER_CLA 15
+#define DHCPV6_OPTION_VENDOR_C 16
+#define DHCPV6_OPTION_VENDOR_O 17
+#define DHCPV6_OPTION_INTERFAC 18
+#define DHCPV6_OPTION_RECONF_M 19
+#define DHCPV6_OPTION_RECONF_A 20
+#define DHCPV6_OPTION_DNS_SERVERS      23
+#define DHCPV6_OPTION_DOMAIN_LIST      24
+#define DHCPV6_OPTION_BOOT_URL 59
+
+/* DHCPv6 status codes */
+#define DHCP_STATUSCODE_SUCCESS                0
+#define DHCP_STATUSCODE_UNSPECFAIL     1
+#define DHCP_STATUSCODE_NOADDRAVAIL    2
+#define DHCP_STATUSCODE_NOBINDING      3
+#define DHCP_STATUSCODE_NOTONLINK      4
+#define DHCP_STATUSCODE_USEMULTICAST   5
+#define DHCPV6_STATE_SELECT            6
+
+/* DUID types  */
+#define DUID_LLT       1 /* DUID based on Link-layer Address Plus Time */
+#define DUID_EN                2 /* DUID based on Assigned by Vendor Based on 
Enterprise Number */
+#define DUID_LL                3 /* DUID based on Link-layer Address */
+
+/* Prototypes */
+void dhcpv6_generate_transaction_id(void);
+int32_t dhcpv6 ( char *ret_buffer, void *fn_ip);
+uint32_t handle_dhcpv6(uint8_t * , int32_t);
+
+struct dhcp6_gen_option {
+       uint16_t code;
+       uint16_t length;
+};
+
+struct client_identifier {
+       uint16_t code;
+       uint16_t length;
+       uint16_t duid_type;
+       uint16_t hardware_type;
+       uint8_t mac[6];
+};
+
+struct server_identifier {
+       uint16_t code;
+       uint16_t length;
+       uint16_t duid_type;
+       uint16_t hardware_type;
+       uint32_t time;
+       uint8_t mac[6];
+};
+
+#define DHCPV6_OPTREQUEST_NUMOPTS 3
+
+struct dhcp_info_request {
+       struct client_identifier client_id;
+       struct elapsed_time {
+               uint16_t code;
+               uint16_t length;
+               uint16_t time;
+       } el_time;
+       struct option_request {
+               uint16_t code;
+               uint16_t length;
+               uint16_t option_code[DHCPV6_OPTREQUEST_NUMOPTS];
+       } option_request_option;
+};
+
+struct dhcp_message_header {
+       uint8_t type;              /* Message type   */
+       uint8_t transaction_id[3]; /* Transaction id */
+       struct dhcp_info_request option;
+};
+
+struct dhcp_dns {
+       uint16_t code;
+       uint16_t length;
+       uint8_t p_ip6[16];
+       uint8_t s_ip6[16];
+}__attribute((packed));
+
+struct dhcp_dns_list {
+       uint16_t code;
+       uint16_t length;
+       uint8_t domain[256];
+}__attribute((packed));
+
+struct dhcp_boot_url {
+       uint16_t type;
+       uint16_t length;
+       uint8_t url[256];
+};
+
+struct dhcp_message_reply {
+       uint8_t type;                       /* Message type   */
+       uint8_t transaction_id[3];          /* Transaction id */
+       struct client_identifier client_id;
+       struct server_identifier server_id;
+};
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/dns.c b/pc-bios/s390-ccw/libnet/dns.c
new file mode 100644
index 0000000..a7313a9
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dns.c
@@ -0,0 +1,526 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+/********************** DEFINITIONS & DECLARATIONS ***********************/
+
+#include <dns.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+
+#include <ethernet.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <udp.h>
+
+#define DNS_FLAG_MSGTYPE    0xF800     /**< Message type mask (opcode) */
+#define DNS_FLAG_SQUERY     0x0000     /**< Standard query type        */
+#define DNS_FLAG_SRESPONSE  0x8000     /**< Standard response type     */
+#define DNS_FLAG_RD         0x0100  /**< Recursion desired flag     */
+#define DNS_FLAG_RCODE      0x000F     /**< Response code mask
+                                         (stores err.cond.) code    */
+#define DNS_RCODE_NERROR    0       /**< "No errors" code           */
+
+#define DNS_QTYPE_A         1       /**< A 32-bit IP record type */
+#define DNS_QTYPE_AAAA      0x1c    /**< 128-bit IPv6 record type */
+#define DNS_QTYPE_CNAME     5       /**< Canonical name record type */
+
+#define DNS_QCLASS_IN       1       /**< Query class for internet msgs */
+
+/** \struct dnshdr
+ *  A header for DNS-messages (see RFC 1035, paragraph 4.1.1).
+ *  <p>
+ *  DNS-message consist of DNS-header and 4 optional sections,
+ *  arranged in the following order:<ul>
+ *    <li> DNS-header
+ *    <li> question section
+ *    <li> answer section
+ *    <li> authority section
+ *    <li> additional section
+ *  </ul>
+ */
+struct dnshdr {
+       uint16_t   id;      /**< an identifier used to match up replies */
+       uint16_t   flags;   /**< contains op_code, err_code, etc. */
+       uint16_t   qdcount; /**< specifies the number of entries in the 
+                                question section */
+       uint16_t   ancount; /**< specifies the number of entries in the 
+                                answer section */
+       uint16_t   nscount; /**< specifies the number of entries in the
+                                authority section */
+       uint16_t   arcount; /**< specifies the number of entries in the 
+                                additional section */
+};
+
+
+/***************************** PROTOTYPES ********************************/
+
+static void
+dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version);
+
+static void
+fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version);
+
+static uint8_t *
+dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name);
+
+static int8_t
+urltohost(char * url, char * host_name);
+
+static int8_t
+hosttodomain(char * host_name, char * domain_name);
+
+/**************************** LOCAL VARIABLES ****************************/
+
+static uint8_t ether_packet[ETH_MTU_SIZE];
+static int32_t dns_server_ip       = 0;
+static uint8_t dns_server_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0};
+static int32_t dns_result_ip       = 0;
+static uint8_t dns_result_ipv6[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0};
+static int8_t  dns_error           = 0;        /**< Stores error code or 0 */
+static int8_t  dns_domain_name[0x100];       /**< Raw domain name        */
+static int8_t  dns_domain_cname[0x100];      /**< Canonical domain name  */
+
+/**************************** IMPLEMENTATION *****************************/
+
+/**
+ * DNS: Initialize the environment for DNS client.
+ *      To perfrom DNS-queries use the function dns_get_ip.
+ *
+ * @param  device_socket a socket number used to send and receive packets
+ * @param  server_ip     DNS-server IPv4 address (e.g. 127.0.0.1)
+ * @return               TRUE in case of successful initialization;
+ *                       FALSE in case of fault (e.g. can't obtain MAC).
+ * @see                  dns_get_ip
+ */
+int8_t
+dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], uint8_t 
ip_version)
+{
+       if(ip_version == 6)
+               memcpy(dns_server_ipv6, _dns_server_ipv6, 16);
+       else
+               dns_server_ip = _dns_server_ip;
+       return 0;
+}
+
+/**
+ * DNS: For given URL retrieves IPv4/IPv6 from DNS-server.
+ *      <p>
+ *      URL can be given in one of the following form: <ul>
+ *      <li> scheme with full path with (without) user and password
+ *           <br>(e.g. "http://user:address@hidden/url-path";);
+ *      <li> host name with url-path
+ *           <br>(e.g. "www.host.org/url-path");
+ *      <li> nothing but host name
+ *           <br>(e.g. "www.host.org");
+ *      </ul>
+ *
+ * @param  fd        socket descriptor
+ * @param  url       the URL to be resolved
+ * @param  domain_ip In case of SUCCESS stores extracted IP.
+ *                   In case of FAULT stores zeros (0.0.0.0).
+ * @return           TRUE - IP successfuly retrieved;
+ *                   FALSE - error condition occurs.
+ */
+int8_t
+dns_get_ip(int fd, char* url, uint8_t * domain_ip, uint8_t ip_version)
+{
+       /* this counter is used so that we abort after 30 DNS request */
+       int32_t i;
+       /* this buffer stores host name retrieved from url */
+       static int8_t host_name[0x100];
+
+       (* domain_ip) = 0;
+
+       // Retrieve host name from URL
+       if (!urltohost(url, (char *) host_name)) {
+               printf("\nERROR:\t\t\tBad URL!\n");
+               return 0;
+       }
+
+       // Reformat host name into a series of labels
+       if (!hosttodomain((char *) host_name, (char *) dns_domain_name)) {
+               printf("\nERROR:\t\t\tBad host name!\n");
+               return 0;
+       }
+
+       // Check if DNS server is presented and accessible
+       if (dns_server_ip == 0) {
+               printf("\nERROR:\t\t\tCan't resolve domain name "
+                      "(DNS server is not presented)!\n");
+               return 0;
+       }
+
+       // Use DNS-server to obtain IP
+       if (ip_version == 6)
+               memset(dns_result_ipv6, 0, 16);
+       else
+               dns_result_ip = 0;
+       dns_error = 0;
+       strcpy((char *) dns_domain_cname, "");
+
+       for(i = 0; i < 30; ++i) {
+               // Use canonical name in case we obtained it
+               if (strlen((char *) dns_domain_cname))
+                       dns_send_query(fd, dns_domain_cname, ip_version);
+               else
+                       dns_send_query(fd, dns_domain_name, ip_version);
+
+               // setting up a timer with a timeout of one seconds
+               set_timer(TICKS_SEC);
+               do {
+                       receive_ether(fd);
+                       if (dns_error)
+                               return 0; // FALSE - error
+                       if ((dns_result_ip != 0) && (ip_version == 4)) {
+                               memcpy(domain_ip, &dns_result_ip, 4);
+                               return 1; // TRUE - success (domain IP 
retrieved)
+                       }
+                       else if ((dns_result_ipv6[0] != 0) && (ip_version == 
6)) {
+                               memcpy(domain_ip, dns_result_ipv6, 16);
+                               return 1; // TRUE - success (domain IP 
retrieved)
+                       }
+               } while (get_timer() > 0);
+       }
+
+       printf("\nGiving up after %d DNS requests\n", i);
+       return 0; // FALSE - domain name wasn't retrieved
+}
+
+/**
+ * DNS: Handles DNS-messages according to Receive-handle diagram.
+ *      Sets dns_result_ip for given dns_domain_name (see dns_get_ip)
+ *      or signals error condition occurs during DNS-resolving process
+ *      by setting dns_error flag.
+ *
+ * @param  packet     DNS-packet to be handled
+ * @param  packetsize length of the packet
+ * @return            ZERO - packet handled successfully;
+ *                    NON ZERO - packet was not handled (e.g. bad format)
+ * @see               dns_get_ip
+ * @see               receive_ether
+ * @see               dnshdr
+ */
+int32_t
+handle_dns(uint8_t * packet, int32_t packetsize)
+{
+       struct dnshdr * dnsh = (struct dnshdr *) packet;
+       uint8_t * resp_section = packet + sizeof(struct dnshdr);
+       /* This string stores domain name from DNS-packets */
+       static int8_t handle_domain_name[0x100]; 
+       int i;
+
+       // verify ID - is it response for our query?
+       if (dnsh -> id != htons(0x1234))
+               return 0;
+
+       // Is it DNS response?
+       if ((dnsh -> flags & htons(DNS_FLAG_MSGTYPE)) != 
htons(DNS_FLAG_SRESPONSE))
+               return 0;
+
+       // Is error condition occurs? (check error field in incoming packet)
+       if ((dnsh -> flags & htons(DNS_FLAG_RCODE)) != DNS_RCODE_NERROR) {
+               dns_error = 1;
+               return 0;
+       }
+
+       /*        Pass all (qdcount) records in question section         */
+
+       for (i = 0; i < htons(dnsh -> qdcount); i++) {
+               // pass QNAME
+               resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) 
resp_section,
+                                               handle_domain_name);
+               if (resp_section == NULL) {
+                       return -1; // incorrect domain name (bad packet)
+               }
+               // pass QTYPE & QCLASS
+               resp_section += 4;
+       }
+
+       /*       Handle all (ancount) records in answer section          */
+
+       for (i = 0; i < htons(dnsh -> ancount); i++) {
+               // retrieve domain name from the packet
+               resp_section = dns_extract_name((uint8_t *) dnsh, (int8_t *) 
resp_section,
+                                               handle_domain_name);
+
+               if (resp_section == NULL) {
+                       return -1; // incorrect domain name (bad packet)
+               }
+
+               // Check the class of the query (should be IN for Internet)
+               if (* (uint16_t *) (resp_section + 2) == htons(DNS_QCLASS_IN)) {
+                       // check if retrieved name fit raw or canonical domain 
name
+                       if (!strcmp((char *) handle_domain_name, (char *) 
dns_domain_name) ||
+                               !strcmp((char *) handle_domain_name, (char *) 
dns_domain_cname)) {
+                               switch (htons(* (uint16_t *) resp_section)) {
+
+                               case DNS_QTYPE_A :
+                                       // rdata contains IP
+                                       dns_result_ip = htonl(* (uint32_t *) 
(resp_section + 10));
+                                       return 0; // IP successfully obtained
+
+                               case DNS_QTYPE_CNAME :
+                                       // rdata contains canonical name, store 
it for further requests
+                                       if (dns_extract_name((uint8_t *) dnsh, 
(int8_t *) resp_section + 10,
+                                                            dns_domain_cname) 
== NULL) {
+                                               // incorrect domain name (bad 
packet)
+                                               return -1;
+                                       }
+                                       break;
+                               case DNS_QTYPE_AAAA :
+                                       memcpy(dns_result_ipv6, (resp_section + 
10), 16);
+                                       return 0; // IP successfully obtained
+                               }
+                       }
+                       // continue with next record in answer section
+                       resp_section += htons(* (uint16_t *) (resp_section + 
8)) + 10;
+               }
+       }
+       return 0; // Packet successfully handled but IP wasn't obtained
+}
+
+/**
+ * DNS: Sends a standard DNS-query (read request package) to a DNS-server.
+ *      DNS-server respones with host IP or signals some error condition.
+ *      Responses from the server are handled by handle_dns function.
+ *
+ * @param  fd          socket descriptor
+ * @param  domain_name the domain name given as series of labels preceded
+ *                     with length(label) and terminated with 0  
+ *                     <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0")
+ * @see                handle_dns
+ */
+static void
+dns_send_query(int fd, int8_t * domain_name, uint8_t ip_version)
+{
+       int qry_len = strlen((char *) domain_name) + 5;
+       int iphdr_len = (ip_version == 4) ? sizeof(struct iphdr) : 
sizeof(struct ip6hdr);
+       ip6_addr_t server_ipv6;
+
+       uint32_t packetsize = iphdr_len +
+                             sizeof(struct udphdr) + sizeof(struct dnshdr) +
+                             qry_len;
+
+       memset(ether_packet, 0, packetsize);
+       fill_dnshdr(&ether_packet[
+                   iphdr_len + sizeof(struct udphdr)],
+                   domain_name,
+                   ip_version);
+       fill_udphdr(&ether_packet[iphdr_len],
+                   sizeof(struct dnshdr) +
+                   sizeof(struct udphdr) + qry_len,
+                   UDPPORT_DNSC, UDPPORT_DNSS);
+       if (ip_version == 4) {
+               fill_iphdr(ether_packet,
+                          sizeof(struct dnshdr) + sizeof(struct udphdr) +
+                          iphdr_len + qry_len,
+                          IPTYPE_UDP, 0, dns_server_ip);
+       } else {
+               memcpy(server_ipv6.addr, dns_server_ipv6, 16);
+               fill_ip6hdr(ether_packet,
+                           sizeof(struct dnshdr) + sizeof(struct udphdr) + 
qry_len,
+                           IPTYPE_UDP, get_ipv6_address(),
+                           &server_ipv6);
+       }
+
+       send_ip(fd, ether_packet, packetsize);
+}
+
+/**
+ * DNS: Creates standard DNS-query package. Places DNS-header
+ *      and question section in a packet and fills it with
+ *      corresponding information.
+ *      <p>
+ *      Use this function with similar functions for other network layers
+ *      (fill_udphdr, fill_iphdr, fill_ethhdr).
+ *
+ * @param  packet      Points to the place where ARP-header must be placed.
+ * @param  domain_name the domain name given as series of labels preceded
+ *                     with length(label) and terminated with 0  
+ *                     <br>(e.g. "\3,w,w,w,\4,h,o,s,t,\3,o,r,g,\0")
+ * @see                fill_udphdr
+ * @see                fill_iphdr
+ * @see                fill_ethhdr
+ */
+static void
+fill_dnshdr(uint8_t * packet, int8_t * domain_name, uint8_t ip_version)
+{
+       struct dnshdr * dnsh = (struct dnshdr *) packet;
+       uint8_t * qry_section = packet + sizeof(struct dnshdr);
+
+       dnsh -> id = htons(0x1234);
+       dnsh -> flags = htons(DNS_FLAG_SQUERY) | htons(DNS_FLAG_RD);
+       dnsh -> qdcount = htons(1);
+
+       strcpy((char *) qry_section, (char *) domain_name);
+       qry_section += strlen((char *) domain_name) + 1;
+
+       // fill QTYPE (ask for IP)
+       if (ip_version == 4)
+               * (uint16_t *) qry_section = htons(DNS_QTYPE_A);
+       else
+               * (uint16_t *) qry_section = htons(DNS_QTYPE_AAAA);
+       qry_section += 2;
+       // fill QCLASS (IN is a standard class for Internet)
+       * (uint16_t *) qry_section = htons(DNS_QCLASS_IN);
+}
+
+/**
+ * DNS: Extracts domain name from the question or answer section of
+ *      the DNS-message. This function is need to support message  
+ *      compression requirement (see RFC 1035, paragraph 4.1.4).
+ *
+ * @param  dnsh        Points at the DNS-header.
+ * @param  head        Points at the beginning of the domain_name
+ *                     which has to be extracted.
+ * @param  domain_name In case of SUCCESS this string stores extracted name.
+ *                     In case of FAULT this string is empty.
+ * @return             NULL in case of FAULT (domain name > 255 octets); 
+ *                     otherwise pointer to the data following the name.
+ * @see                dnshdr
+ */
+static uint8_t *
+dns_extract_name(uint8_t * dnsh, int8_t * head, int8_t * domain_name)
+{
+       int8_t * tail = domain_name;
+       int8_t * ptr = head;
+       int8_t * next_section = NULL;
+
+       while (1) {
+               if ((ptr[0] & 0xC0) == 0xC0) {
+                       // message compressed (reference is used)
+                       next_section = ptr + 2;
+                       ptr = (int8_t *) dnsh + (htons(* (uint16_t *) ptr) & 
0x3FFF);
+                       continue;
+               }
+               if (ptr[0] == 0) {
+                       // message termination
+                       tail[0] = 0;
+                       ptr += 1;
+                       break;
+               }
+               // maximum length for domain name is 255 octets w/o termination 
sym
+               if (tail - domain_name + ptr[0] + 1 > 255) {
+                       strcpy((char *) domain_name, "");
+                       return NULL;
+               }
+               memcpy(tail, ptr, ptr[0] + 1);
+               tail += ptr[0] + 1;
+               ptr += ptr[0] + 1;
+       }
+
+       if (next_section == NULL)
+               next_section = ptr;
+
+       return (uint8_t *) next_section;
+}
+
+/**
+ * DNS: Parses URL and returns host name.
+ *      Input string can be given as: <ul>
+ *      <li> scheme with full path with (without) user and password
+ *           <br>(e.g. "http://user:address@hidden/url-path";);
+ *      <li> host name with url-path
+ *           <br>(e.g. "www.host.org/url-path");
+ *      <li> nothing but host name
+ *           <br>(e.g. "www.host.org");
+ *      </ul>
+ *
+ * @param  url        string that stores incoming URL
+ * @param  host_name  In case of SUCCESS this string stores the host name,
+ *                    In case of FAULT this string is empty.
+ * @return            TRUE - host name retrieved,
+ *                    FALSE - host name > 255 octets or empty.
+ */
+static int8_t
+urltohost(char * url, char * host_name)
+{
+       uint16_t length1;
+       uint16_t length2;
+
+       strcpy(host_name, "");
+
+       if (strstr(url, "://") != NULL)
+               url = strstr(url, "//") + 2;  // URL
+
+       if (strstr(url, "@") != NULL) // truncate user & password
+               url = strstr(url, "@") + 1;
+
+       if (strstr(url, "/") != NULL) // truncate url path
+               length1 = strstr(url, "/") - url;
+       else
+               length1 = strlen(url);
+
+       if (strstr(url, ":") != NULL) // truncate port path
+               length2 = strstr(url, ":") - url;
+       else
+               length2 = strlen(url);
+
+       if(length1 > length2)
+               length1 = length2;
+
+       if (length1 == 0)
+               return 0; // string is empty
+       if(length1 >= 256)
+               return 0; // host name is too big
+
+       strncpy(host_name, url, length1);
+       host_name[length1] = 0;
+
+       return 1; // Host name is retrieved
+}
+
+/**
+ * DNS: Transforms host name string into a series of labels
+ *      each of them preceded with length(label). 0 is a terminator.
+ *      "www.domain.dom" -> "\3,w,w,w,\6,d,o,m,a,i,n,\3,c,o,m,\0"
+ *      <p>
+ *      This format is used in DNS-messages.
+ *
+ * @param  host_name   incoming string with the host name
+ * @param  domain_name resulting string with series of labels
+ *                     or empty string in case of FAULT
+ * @return             TRUE - host name transformed,
+ *                     FALSE - host name > 255 octets or label > 63 octets.
+ */
+static int8_t
+hosttodomain(char * host_name, char * domain_name)
+{
+       char * domain_iter = domain_name;
+       char * host_iter   = host_name;
+
+       strcpy(domain_name, "");
+
+       if(strlen(host_name) > 255)
+               return 0; // invalid host name (refer to RFC 1035)
+
+       for(; 1; ++host_iter) {
+               if(*host_iter != '.' && *host_iter != 0)
+                       continue;
+               *domain_iter = host_iter - host_name;
+               if (*domain_iter > 63) {
+                       strcpy(domain_name, "");
+                       return 0; // invalid host name (refer to RFC 1035)
+               }
+               ++domain_iter;
+               strncpy(domain_iter, host_name, host_iter - host_name);
+               domain_iter += (host_iter - host_name);
+               if(*host_iter == 0) {
+                       *domain_iter = 0;
+                       break;
+               }
+               host_name = host_iter + 1;
+       }
+       return 1; // ok
+}
diff --git a/pc-bios/s390-ccw/libnet/dns.h b/pc-bios/s390-ccw/libnet/dns.h
new file mode 100644
index 0000000..b8756af
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/dns.h
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * 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 _DNS_H_
+#define _DNS_H_
+
+#include <stdint.h>
+
+/* Initialize the environment for DNS client. */
+extern int8_t dns_init(uint32_t _dns_server_ip, uint8_t _dns_server_ipv6[16], 
uint8_t ip_version);
+
+/* For given URL retrieves IPv4 from DNS-server. */
+extern int8_t dns_get_ip(int fd, char * url, uint8_t * domain_ip, uint8_t 
ip_version);
+
+/* Handles DNS-packets, which are detected by receive_ether. */
+extern int32_t handle_dns(uint8_t * packet, int32_t packetsize);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/ethernet.c 
b/pc-bios/s390-ccw/libnet/ethernet.c
new file mode 100644
index 0000000..1e03a0b
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ethernet.c
@@ -0,0 +1,189 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+
+/******************************* ALGORITHMS ******************************/
+
+/** \file netbase.c <pre>
+ * *********************** Receive-handle diagram *************************
+ *
+ * Note: Every layer calls out required upper layer
+ *
+ * lower
+ *  | MAC/LLC     Receive packet (receive_ether)
+ *  |                           |
+ *  | NETWORK       +-----------+---------+
+ *  |               |                     |
+ *  |           IPv4 (handle_ipv4)    IPv6 (handle_ipv4)
+ *  |           ARP  (handle_arp)     ICMP & NDP
+ *  |           ICMP                      |
+ *  |                 |                   |
+ *  |                 +---------+---------+
+ *  |                           |
+ *  | TRANSPORT       +---------+---------+
+ *  |                 |                   |
+ *  |              TCP (handle_tcp)    UDP (handle_udp)
+ *  |                                     |
+ *  | APPLICATION        +----------------+-----------+
+ *  V                    |                            |
+ * upper               DNS (handle_dns)      BootP / DHCP (handle_bootp_client)
+ *
+ * ************************************************************************
+ * </pre> */
+
+
+/************************ DEFINITIONS & DECLARATIONS *********************/
+
+#include <ethernet.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <ipv4.h>
+#include <ipv6.h>
+
+
+/****************************** LOCAL VARIABLES **************************/
+
+static uint8_t ether_packet[ETH_MTU_SIZE];
+static uint8_t own_mac[6] = {0, 0, 0, 0, 0, 0};
+static uint8_t multicast_mac[] = {0x01, 0x00, 0x5E};
+static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+/****************************** IMPLEMENTATION ***************************/
+
+/**
+ * Ethernet: Set the own MAC address to initializes ethernet layer.
+ *
+ * @param  own_mac  own hardware-address (MAC)
+ */
+void set_mac_address(const uint8_t * _own_mac)
+{
+       if (_own_mac)
+               memcpy(own_mac, _own_mac, 6);
+       else
+               memset(own_mac, 0, 6);
+}
+
+/**
+ * Ethernet: Set the own MAC address to initializes ethernet layer.
+ *
+ * @return  own hardware-address (MAC)
+ */
+const uint8_t *get_mac_address(void)
+{
+       return own_mac;
+}
+
+/**
+ * Ethernet: Check if given multicast address is a multicast MAC address
+ *           starting with 0x3333
+ *
+ * @return  true or false
+ */
+static uint8_t is_multicast_mac(uint8_t * mac)
+{
+
+       uint16_t mc = 0x3333;
+       if (memcmp(mac, &mc, 2) == 0)
+              return 1;
+
+       return 0;
+}
+
+/**
+ * Ethernet: Receives an ethernet-packet and handles it according to
+ *      Receive-handle diagram.
+ *
+ * @param  fd        socket fd
+ * @return  ZERO - packet was handled or no packets received;
+ *          NON ZERO - error condition occurs.
+ */
+int32_t receive_ether(int fd)
+{
+       int32_t bytes_received;
+       struct ethhdr * ethh;
+
+       memset(ether_packet, 0, ETH_MTU_SIZE);
+       bytes_received = recv(fd, ether_packet, ETH_MTU_SIZE, 0);
+
+       if (!bytes_received) // No messages
+               return 0;
+
+       if (bytes_received < 0)
+               return -1; /* recv() failed */
+
+       if ((size_t) bytes_received < sizeof(struct ethhdr))
+               return -1; // packet is too small
+
+       ethh = (struct ethhdr *) ether_packet;
+
+       if(memcmp(ethh->dest_mac, broadcast_mac, 6) != 0
+       && memcmp(ethh->dest_mac, multicast_mac, 3) != 0
+       && memcmp(ethh->dest_mac, own_mac, 6      ) != 0
+       && !is_multicast_mac(ethh->dest_mac))
+               return -1; // packet is too small
+
+       switch (htons(ethh -> type)) {
+       case ETHERTYPE_IP:
+               return handle_ipv4(fd, (uint8_t*) (ethh + 1),
+                                  bytes_received - sizeof(struct ethhdr));
+
+       case ETHERTYPE_IPv6:
+               return handle_ipv6(fd, ether_packet + sizeof(struct ethhdr),
+                               bytes_received - sizeof(struct ethhdr));
+
+       case ETHERTYPE_ARP:
+               return handle_arp(fd, (uint8_t*) (ethh + 1),
+                          bytes_received - sizeof(struct ethhdr));
+       default:
+               break;
+       }
+       return -1; // unknown protocol
+}
+
+/**
+ * Ethernet: Sends an ethernet frame via the initialized file descriptor.
+ *
+ * @return number of transmitted bytes
+ */
+int
+send_ether(int fd, void* buffer, int len)
+{
+       return send(fd, buffer, len, 0);
+}
+
+/**
+ * Ethernet: Creates Ethernet-packet. Places Ethernet-header in a packet and
+ *           fills it with corresponding information.
+ *           <p>
+ *           Use this function with similar functions for other network layers
+ *           (fill_arphdr, fill_iphdr, fill_udphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param  packet      Points to the place where eth-header must be placed.
+ * @param  eth_type    Type of the next level protocol (e.g. IP or ARP).
+ * @param  src_mac     Sender MAC address
+ * @param  dest_mac    Receiver MAC address
+ * @see                ethhdr
+ * @see                fill_arphdr
+ * @see                fill_iphdr
+ * @see                fill_udphdr
+ * @see                fill_dnshdr
+ * @see                fill_btphdr
+ */
+void fill_ethhdr(uint8_t * packet, uint16_t eth_type,
+                const uint8_t * src_mac, const uint8_t * dest_mac)
+{
+       struct ethhdr * ethh = (struct ethhdr *) packet;
+
+       ethh -> type = htons(eth_type);
+       memcpy(ethh -> src_mac, src_mac, 6);
+       memcpy(ethh -> dest_mac, dest_mac, 6);
+}
diff --git a/pc-bios/s390-ccw/libnet/ethernet.h 
b/pc-bios/s390-ccw/libnet/ethernet.h
new file mode 100644
index 0000000..e541c8f
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ethernet.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * 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 _ETHERNET_H
+#define _ETHERNET_H
+
+#include <stdint.h>
+
+#define ETH_MTU_SIZE     1518   /**< Maximum Transfer Unit         */
+#define ETH_ALEN            6   /**< HW address length             */
+#define ETHERTYPE_IP   0x0800
+#define ETHERTYPE_IPv6 0x86DD
+#define ETHERTYPE_ARP  0x0806
+
+/** \struct ethhdr
+ *  A header for Ethernet-packets.
+ */
+struct ethhdr {
+       uint8_t dest_mac[ETH_ALEN];   /**< Destination HW address        */
+       uint8_t src_mac[ETH_ALEN];    /**< Source HW address             */
+       uint16_t type;                /**< Next level protocol type      */
+};
+
+/* Initializes ethernet layer */
+extern void set_mac_address(const uint8_t * own_mac);
+extern const uint8_t * get_mac_address(void);
+
+/* Receives and handles packets, according to Receive-handle diagram */
+extern int32_t receive_ether(int fd);
+
+/* Sends an ethernet frame. */
+extern int send_ether(int fd, void* buffer, int len);
+
+/* fills ethernet header */
+extern void fill_ethhdr(uint8_t * packet, uint16_t eth_type,
+                        const uint8_t * src_mac, const uint8_t * dest_mac);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/icmpv6.c b/pc-bios/s390-ccw/libnet/icmpv6.c
new file mode 100644
index 0000000..d44ce84
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/icmpv6.c
@@ -0,0 +1,409 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include "ethernet.h"
+#include "ipv6.h"
+#include "icmpv6.h"
+#include "ndp.h"
+#include "dhcpv6.h"
+
+static int ra_received = 0;
+
+/**
+ * NET:
+ * @param  fd           socket fd
+ */
+void
+send_router_solicitation (int fd)
+{
+       ip6_addr_t dest_addr;
+       uint8_t *ether_packet;
+       struct packeth headers;
+
+       ether_packet = malloc(ETH_MTU_SIZE);
+       if (!ether_packet) {
+               fprintf(stderr, "send_router_solicitation: Out of memory\n");
+               return;
+       }
+
+       headers.ip6h   = (struct ip6hdr *) (ether_packet + sizeof(struct 
ethhdr));
+       headers.icmp6h = (struct icmp6hdr *) (ether_packet +
+                         sizeof(struct ethhdr) +
+                         sizeof(struct ip6hdr));
+
+       /* Destination is "All routers multicast address" (link-local) */
+       dest_addr.part.prefix       = 0xff02000000000000ULL;
+       dest_addr.part.interface_id = 2;
+
+       /* Fill IPv6 header */
+       fill_ip6hdr (ether_packet + sizeof(struct ethhdr),
+                    ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation),
+                    0x3a, //ICMPV6
+                    get_ipv6_address(), &dest_addr);
+
+       /* Fill ICMPv6 message */
+       headers.icmp6h->type = ICMPV6_ROUTER_SOLICITATION;
+       headers.icmp6h->code = 0;
+       headers.icmp6h->icmp6body.router_solicit.lladdr.type    = 1;
+       headers.icmp6h->icmp6body.router_solicit.lladdr.length  = 1;
+       memcpy( &(headers.icmp6h->icmp6body.router_solicit.lladdr.mac),
+               get_mac_address(), 6);
+
+       send_ip (fd, headers.ip6h, sizeof(struct ip6hdr) +
+                  ICMPv6_HEADER_SIZE + sizeof(struct router_solicitation));
+
+       free(ether_packet);
+}
+
+/**
+ * NET: Process prefix option in Router Advertisements
+ *
+ * @param  ip6_packet  pointer to an IPv6 packet
+ */
+static void
+handle_prefixoption (uint8_t *option)
+{
+       ip6_addr_t prefix;
+       struct ip6addr_list_entry *new_address;
+       struct option_prefix *prefix_option;
+       struct prefix_info *prfx_info;
+
+       prefix_option = (struct option_prefix *) option;
+       memcpy( &(prefix.addr), &(prefix_option->prefix.addr), 
IPV6_ADDR_LENGTH);
+
+       /* Link-local adresses in RAs are nonsense */
+       if (ip6_is_linklocal(&prefix))
+               return;
+
+       if (prefix_option->preferred_lifetime > prefix_option->valid_lifetime)
+               return;
+
+       /* Add address created from prefix to IPv6 address list */
+       new_address = ip6_prefix2addr (prefix);
+       if (!new_address)
+               return;
+
+       /* Process only prefixes we don't already have an adress from */
+       if (!unknown_prefix (&new_address->addr)) {
+               return;
+       }
+
+       /* Fill struct prefix_info from data in RA and store it in new_address 
*/
+       prfx_info = ip6_create_prefix_info();
+       if (!prfx_info)
+               return;
+       memcpy (&(new_address->prfx_info), prfx_info, sizeof(struct 
prefix_info));
+
+       /* Add prefix received in RA to list of known prefixes */
+       ip6addr_add (new_address);
+}
+
+/**
+ * NET: Process source link layer addresses in Router Advertisements
+ *
+ * @param  ip6_packet  pointer to an IPv6 packet
+ */
+static void
+handle_source_lladdr ( struct option_ll_address *option, struct router *rtr)
+{
+       memcpy (&(rtr->mac), &(option->mac), 6);
+}
+
+/**
+ * NET: Process ICMPv6 options in Router Advertisements
+ *
+ * @param  ip6_packet  pointer to an IPv6 packet
+ */
+static void
+process_ra_options (uint8_t *option, int32_t option_length, struct router *r)
+{
+       while (option_length > 0) {
+               switch (*option) {
+                       case ND_OPTION_SOURCE_LL_ADDR:
+                               handle_source_lladdr ((struct option_ll_address 
*) option, r);
+                               break;
+                       case ND_OPTION_PREFIX_INFO:
+                               handle_prefixoption(option);
+                               break;
+                       default:
+                               break;
+               }
+               //option+1 is the length field. length is in units of 8 bytes
+               option_length = option_length - (*(option+1) * 8);
+               option = option + (*(option+1) * 8);
+       }
+
+       return;
+}
+
+/**
+ * NET: Process Router Advertisements
+ *
+ * @param  ip6_packet  pointer to an IPv6 packet
+ */
+static void
+handle_ra (struct icmp6hdr *icmp6h, uint8_t *ip6_packet)
+{
+       uint8_t  *first_option;
+       int32_t option_length;
+       struct ip6hdr *ip6h;
+       struct router_advertisement *ra;
+       struct router *rtr;
+       ip6_addr_t *rtr_ip;
+       uint8_t rtr_mac[] = {0, 0, 0, 0, 0, 0};
+
+       ip6h = (struct ip6hdr *) ip6_packet;
+       ra = (struct router_advertisement *) &icmp6h->icmp6body.ra;
+       rtr_ip = (ip6_addr_t *) &ip6h->src;
+
+       rtr = find_router (&(ip6h->src));
+       if (!rtr) {
+               rtr = router_create (rtr_mac, rtr_ip);
+               router_add (rtr);
+       }
+
+       /* store info from router advertisement in router struct */
+       rtr->lifetime = ra->router_lifetime;
+       rtr->reachable_time = ra->reachable_time;
+       rtr->retrans_timer = ra->retrans_timer;
+
+       /* save flags concerning address (auto-) configuration */
+       ip6_state.managed_mode = ra->flags.managed;
+       ip6_state.other_config = ra->flags.other;
+
+       /* Process ICMPv6 options in Router Advertisement */
+       first_option = (uint8_t *) icmp6h + ICMPv6_HEADER_SIZE + 12;
+       option_length =  (uint8_t *) icmp6h + ip6h->pl - first_option;
+       process_ra_options( (uint8_t *) first_option, option_length, rtr);
+
+       ra_received = 1;
+}
+
+int is_ra_received(void)
+{
+       return ra_received;
+}
+
+/**
+ * NET:
+ *
+ * @param  fd         socket fd
+ * @param  ip6_addr_t *dest_ip6
+ */
+void
+send_neighbour_solicitation (int fd, ip6_addr_t *dest_ip6)
+{
+       ip6_addr_t snma;
+       uint8_t *ether_packet;
+       struct  packeth headers;
+
+       ether_packet = malloc(ETH_MTU_SIZE);
+       if (!ether_packet) {
+               fprintf(stderr, "send_neighbour_solicitation: Out of memory\n");
+               return;
+       }
+
+       memset(ether_packet, 0, ETH_MTU_SIZE);
+       headers.ethh   = (struct ethhdr *) ether_packet;
+       headers.ip6h   = (struct ip6hdr *) (ether_packet + sizeof(struct 
ethhdr));
+       headers.icmp6h = (struct icmp6hdr *) (ether_packet +
+                         sizeof(struct ethhdr) +
+                         sizeof(struct ip6hdr));
+
+       /* Fill IPv6 header */
+       snma.part.prefix       = IPV6_SOLIC_NODE_PREFIX;
+       snma.part.interface_id = IPV6_SOLIC_NODE_IFACE_ID;
+       snma.addr[13]          = dest_ip6->addr[13];
+       snma.addr[14]          = dest_ip6->addr[14];
+       snma.addr[15]          = dest_ip6->addr[15];
+       fill_ip6hdr((uint8_t *) headers.ip6h,
+                   ICMPv6_HEADER_SIZE + sizeof(struct neighbour_solicitation),
+                   0x3a, //ICMPv6
+                   get_ipv6_address(), &snma);
+
+       /* Fill ICMPv6 message */
+       headers.icmp6h->type = ICMPV6_NEIGHBOUR_SOLICITATION;
+       headers.icmp6h->code = 0;
+       memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.target),
+               dest_ip6, IPV6_ADDR_LENGTH );
+       headers.icmp6h->icmp6body.nghb_solicit.lladdr.type    = 1;
+       headers.icmp6h->icmp6body.nghb_solicit.lladdr.length  = 1;
+       memcpy( &(headers.icmp6h->icmp6body.nghb_solicit.lladdr.mac),
+               get_mac_address(), 6);
+
+       send_ip (fd, ether_packet + sizeof(struct ethhdr),
+                  sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE +
+                  sizeof(struct neighbour_solicitation));
+
+       free(ether_packet);
+}
+
+/**
+ * NET:
+ *
+ * @param  fd           socket fd
+ * @param  ip6_packet  pointer to an IPv6 packet
+ * @param  icmp6hdr    pointer to the icmp6 header in ip6_packet
+ * @param  na_flags    Neighbour advertisment flags
+ */
+static void
+send_neighbour_advertisement (int fd, struct neighbor *target)
+{
+       struct na_flags na_adv_flags;
+       uint8_t *ether_packet;
+       struct  packeth headers;
+
+       ether_packet = malloc(ETH_MTU_SIZE);
+       if (!ether_packet) {
+               fprintf(stderr, "send_neighbour_advertisement: Out of 
memory\n");
+               return;
+       }
+
+       headers.ip6h   = (struct ip6hdr *) (ether_packet + sizeof(struct 
ethhdr));
+       headers.icmp6h = (struct icmp6hdr *) (ether_packet +
+                         sizeof(struct ethhdr) +
+                         sizeof(struct ip6hdr));
+
+       /* Fill IPv6 header */
+       fill_ip6hdr(ether_packet + sizeof(struct ethhdr),
+                   ICMPv6_HEADER_SIZE + sizeof(struct neighbour_advertisement),
+                   0x3a, //ICMPv6
+                   get_ipv6_address(), (ip6_addr_t *) &(target->ip.addr));
+
+       /* Fill ICMPv6 message */
+       memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target),
+               &(target->ip.addr), IPV6_ADDR_LENGTH );
+       headers.icmp6h->icmp6body.nghb_adv.lladdr.type    = 1;
+       headers.icmp6h->icmp6body.nghb_adv.lladdr.length  = 1;
+       memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac),
+               get_mac_address(), 6);
+
+       na_adv_flags.is_router = 0;
+       na_adv_flags.na_is_solicited = 1;
+       na_adv_flags.override = 1;
+
+       headers.icmp6h->type = ICMPV6_NEIGHBOUR_ADVERTISEMENT;
+       headers.icmp6h->code = 0;
+       headers.icmp6h->icmp6body.nghb_adv.router    = na_adv_flags.is_router;
+
+       headers.icmp6h->icmp6body.nghb_adv.solicited = 
na_adv_flags.na_is_solicited;
+       headers.icmp6h->icmp6body.nghb_adv.override  = na_adv_flags.override;
+       headers.icmp6h->icmp6body.nghb_adv.lladdr.type      = 2;
+       headers.icmp6h->icmp6body.nghb_adv.lladdr.length    = 1;
+
+       memset( &(headers.icmp6h->icmp6body.nghb_adv.target), 0,
+               IPV6_ADDR_LENGTH );
+
+       if( na_adv_flags.na_is_solicited ) {
+               memcpy( &(headers.icmp6h->icmp6body.nghb_adv.target),
+                       get_ipv6_address(), IPV6_ADDR_LENGTH);
+       }
+
+       memcpy( &(headers.icmp6h->icmp6body.nghb_adv.lladdr.mac),
+               get_mac_address(), 6);
+
+       send_ip (fd, ether_packet + sizeof(struct ethhdr),
+                  sizeof(struct ip6hdr) + ICMPv6_HEADER_SIZE +
+                  sizeof(struct neighbour_advertisement));
+
+       free(ether_packet);
+}
+
+/**
+ * NET:
+ *
+ * @param  fd           socket fd
+ * @param  ip6_packet  pointer to an IPv6 packet
+ */
+static int8_t
+handle_na (int fd, uint8_t *packet)
+{
+       struct neighbor *n = NULL;
+       struct packeth headers;
+       ip6_addr_t ip;
+
+       headers.ethh = (struct ethhdr *) packet;
+       headers.ip6h = (struct ip6hdr *) (packet + sizeof(struct ethhdr));
+       headers.icmp6h = (struct icmp6hdr *) (packet +
+                                             sizeof(struct ethhdr) +
+                                             sizeof(struct ip6hdr));
+
+       memcpy(&(ip.addr), &(headers.ip6h->src), IPV6_ADDR_LENGTH);
+
+       n = find_neighbor (&ip);
+
+       if (!n) {
+               n= (struct neighbor *)
+                       neighbor_create( packet, &headers );
+               if (!n)
+                       return 0;
+               if (!neighbor_add(n))
+                       return 0;
+       } else {
+               memcpy (&(n->mac), &(headers.ethh->src_mac[0]), 6);
+               n->status = NB_REACHABLE;
+               if (n->eth_len > 0) {
+                       struct ethhdr * ethh = (struct ethhdr *) 
&(n->eth_frame);
+                       memcpy(ethh->dest_mac, &(n->mac), 6);
+                       send_ether (fd, &(n->eth_frame), n->eth_len + 
sizeof(struct ethhdr));
+                       n->eth_len = 0;
+               }
+       }
+
+       return 1;
+}
+
+/**
+ * NET: Handles ICMPv6 messages
+ *
+ * @param  fd           socket fd
+ * @param  ip6_packet  pointer to an IPv6 packet
+ * @param  packetsize  size of ipv6_packet
+ */
+int8_t
+handle_icmpv6 (int fd, struct ethhdr *etherhdr,
+             uint8_t  *ip6_packet)
+{
+
+       struct icmp6hdr *received_icmp6 = NULL;
+       struct ip6hdr *received_ip6     = NULL;
+       struct neighbor target;
+
+       received_ip6 =   (struct ip6hdr *) ip6_packet;
+       received_icmp6 = (struct icmp6hdr *) (ip6_packet +
+                         sizeof(struct ip6hdr));
+       memcpy( &(target.ip.addr), &(received_ip6->src),
+               IPV6_ADDR_LENGTH );
+       memcpy( &(target.mac), etherhdr->src_mac, 6);
+
+       /* process ICMPv6 types */
+       switch(received_icmp6->type) {
+               case ICMPV6_NEIGHBOUR_SOLICITATION:
+                       send_neighbour_advertisement(fd, &target);
+                       break;
+               case ICMPV6_NEIGHBOUR_ADVERTISEMENT:
+                       handle_na(fd, (uint8_t *) ip6_packet - sizeof(struct 
ethhdr));
+                       break;
+               case ICMPV6_ROUTER_ADVERTISEMENT:
+                       handle_ra(received_icmp6, (uint8_t *) received_ip6);
+                       break;
+               default:
+                       return -1;
+       }
+
+       return 1;
+}
diff --git a/pc-bios/s390-ccw/libnet/icmpv6.h b/pc-bios/s390-ccw/libnet/icmpv6.h
new file mode 100644
index 0000000..41b0c70
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/icmpv6.h
@@ -0,0 +1,135 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 _ICMPV6_H_
+#define _ICMPV6_H_
+
+#include <stdint.h>
+#include "ethernet.h"
+#include "ipv6.h"
+
+#define __ICMPV6_DEBUG__
+
+#ifdef __ICMPV6_DEBUG__
+#define ICMPV6_DEBUG_PRINT(format, ...) printf(format, ## __VA_ARGS__)
+#else
+#define ICMPV6_DEBUG_PRINT(format, ...)
+#endif
+
+#define ICMPv6_HEADER_SIZE             4       /* Size of common fields */
+#define IPTYPE_ICMPV6               0x3a
+
+/* Error message types */
+#define ICMPV6_DEST_UNREACHABLE                1       /* Destination 
unreachable */
+#define ICMPV6_PACKET_TOO_BIG          2       /* Packet too big */
+#define ICMPV6_TIME_EXCEEDED           3       /* Time exceeded */
+#define ICMPV6_PARAM_PROBLEM           4       /* Parameter problem */
+
+/* Informational message types */
+#define ICMPV6_ECHO_REQUEST            128     /* Echo request */
+#define ICMPV6_ECHO_REPLY              129     /* Echo reply */
+#define ICMPV6_MCAST_LISTENER_QUERY    130     /* Multicast listener query */
+#define ICMPV6_MCAST_LISTENER_REPORT   131     /* Multicast listener report */
+#define ICMPv6 MCAST_LISTENER_DONE     132     /* Multicast listener done */
+#define ICMPV6_ROUTER_SOLICITATION     133     /* Router solicitation */
+#define ICMPV6_ROUTER_ADVERTISEMENT    134     /* Router advertisement */
+#define ICMPV6_NEIGHBOUR_SOLICITATION  135     /* Neighbor solicitation */
+#define ICMPV6_NEIGHBOUR_ADVERTISEMENT 136     /* Neighbor advertisement */
+#define ICMPV6_REDIRECT_MSG            137     /* Redirect message */
+
+/******** Functions *******************/
+int8_t handle_icmpv6 (int fd, struct ethhdr *etherhdr, uint8_t  *ip6_packet);
+void   send_neighbour_solicitation(int fd, ip6_addr_t *target_ip6);
+void   send_router_solicitation(int fd);
+int    is_ra_received(void);
+
+/* Prefix information */
+struct option_prefix {
+       uint8_t  type;
+       uint8_t  length;
+       uint8_t  prefix_length;
+       uint8_t  onlink:1,
+                autom:1,
+                not_router:1,
+                not_site_prefix:1,
+                reserved:4;
+       uint32_t valid_lifetime;
+       uint32_t preferred_lifetime;
+       uint32_t reserved2;
+       ip6_addr_t prefix;
+} __attribute((packed));
+
+/* Neighbour advertisement/solicitation flags */
+struct na_flags {
+    uint8_t is_router:1,       /* sender (we) is a router */
+           na_is_solicited:1,  /* this NA was solicited (asked for) */
+           override:1,         /* receiver shall override its cache entries */
+           unused:5;
+}__attribute((packed));
+
+/* Source/Target Link-layer address */
+struct option_ll_address{
+        uint8_t  type;
+        uint8_t  length;
+        uint8_t  mac[ETH_ALEN];
+} __attribute((packed));
+
+struct neighbour_solicitation {
+       uint32_t router:1,
+                solicited:1,
+                override:1,
+                reserved:29;
+       ip6_addr_t target;
+       struct option_ll_address lladdr;
+} __attribute((packed));
+
+struct neighbour_advertisement {
+       uint32_t router:1,
+                solicited:1,
+                override:1,
+                reserved:29;
+       ip6_addr_t target;
+       struct option_ll_address lladdr;
+} __attribute((packed));
+
+struct router_solicitation {
+       uint32_t reserved;
+       struct option_ll_address lladdr;
+} __attribute((packed));
+
+struct router_advertisement {
+       uint8_t curr_hop_limit;
+       struct raflags {
+               uint8_t managed:1,
+                       other:1,
+                       reserved:6;
+       } flags;
+       uint16_t router_lifetime;
+       uint32_t reachable_time;
+       uint32_t retrans_timer;
+       struct option_prefix prefix;
+       struct option_ll_address ll_addr;
+} __attribute((packed));
+
+struct icmp6hdr {
+       uint8_t type;
+       uint8_t code;
+       uint16_t checksum;
+       union {
+               struct neighbour_solicitation nghb_solicit;
+               struct neighbour_advertisement nghb_adv;
+               struct router_solicitation router_solicit;
+               struct router_advertisement ra;
+       } icmp6body;
+} __attribute((packed));
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/ipv4.c b/pc-bios/s390-ccw/libnet/ipv4.c
new file mode 100644
index 0000000..3a1a789
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ipv4.c
@@ -0,0 +1,898 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+
+/********************** DEFINITIONS & DECLARATIONS ***********************/
+
+#include <ipv4.h>
+#include <udp.h>
+#include <tcp.h>
+#include <ethernet.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <string.h>
+
+/* ARP Message types */
+#define ARP_REQUEST            1
+#define ARP_REPLY              2
+
+/* ARP talbe size (+1) */
+#define ARP_ENTRIES 10
+
+/* ICMP Message types */
+#define ICMP_ECHO_REPLY            0
+#define ICMP_DST_UNREACHABLE       3
+#define ICMP_SRC_QUENCH            4
+#define ICMP_REDIRECT              5
+#define ICMP_ECHO_REQUEST          8
+#define ICMP_TIME_EXCEEDED        11
+#define ICMP_PARAMETER_PROBLEM    12
+#define ICMP_TIMESTAMP_REQUEST    13
+#define ICMP_TIMESTAMP_REPLY      14
+#define ICMP_INFORMATION_REQUEST  15
+#define ICMP_INFORMATION_REPLY    16
+
+/** \struct arp_entry
+ *  A entry that describes a mapping between IPv4- and MAC-address.
+ */
+typedef struct arp_entry arp_entry_t;
+struct arp_entry {
+       uint32_t ipv4_addr;
+       uint8_t  mac_addr[6];
+       uint8_t  eth_frame[ETH_MTU_SIZE];
+       int      eth_len;
+       int      pkt_pending;
+};
+
+/** \struct icmphdr
+ *  ICMP packet
+ */
+struct icmphdr {
+       unsigned char type;
+       unsigned char code;
+       unsigned short int checksum;
+       union {
+               /* for type 3 "Destination Unreachable" */
+               unsigned int unused;
+               /* for type 0 and 8 */
+               struct echo {
+                       unsigned short int id;
+                       unsigned short int seq;
+               } echo;
+       } options;
+       union {
+               /* payload for destination unreachable */
+               struct dun {
+                       unsigned char iphdr[20];
+                       unsigned char data[64];
+               } dun;
+               /* payload for echo or echo reply */
+               /* maximum size supported is 84 */
+               unsigned char data[84];
+       } payload;
+};
+
+/****************************** PROTOTYPES *******************************/
+
+static unsigned short checksum(unsigned short *packet, int words);
+
+static void arp_send_request(int fd, uint32_t dest_ip);
+
+static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac);
+
+static void fill_arphdr(uint8_t * packet, uint8_t opcode,
+                       const uint8_t * src_mac, uint32_t src_ip,
+                       const uint8_t * dest_mac, uint32_t dest_ip);
+
+static arp_entry_t *lookup_mac_addr(uint32_t ipv4_addr);
+
+static void fill_udp_checksum(struct iphdr *ipv4_hdr);
+
+static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet,
+                         int32_t packetsize);
+
+/****************************** LOCAL VARIABLES **************************/
+
+/* Routing parameters */
+static uint32_t own_ip       = 0;
+static uint32_t multicast_ip = 0;
+static uint32_t router_ip    = 0;
+static uint32_t subnet_mask  = 0;
+
+/* helper variables */
+static uint32_t ping_dst_ip;
+static const uint8_t null_mac_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+static       uint8_t multicast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+/* There are only (ARP_ENTRIES-1) effective entries because
+ * the entry that is pointed by arp_producer is never used.
+ */
+static unsigned int arp_consumer = 0;
+static unsigned int arp_producer = 0;
+static arp_entry_t  arp_table[ARP_ENTRIES];
+
+static uint8_t pending_pkt_frame[ETH_MTU_SIZE];
+static int pending_pkt_len;
+
+/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */
+int   (*send_ip) (int fd, void *, int);
+
+/***************************** IMPLEMENTATION ****************************/
+
+/**
+ * IPv4: Initialize the environment for the IPv4 layer.
+ */
+static void ipv4_init(void)
+{
+       int i;
+
+       ping_dst_ip = 0;
+
+       // clear ARP table
+       arp_consumer = 0;
+       arp_producer = 0;
+       for(i=0; i<ARP_ENTRIES; ++i) {
+               arp_table[i].ipv4_addr = 0;
+               memset(arp_table[i].mac_addr, 0, 6);
+               arp_table[i].eth_len = 0;
+               arp_table[i].pkt_pending = 0;
+       }
+
+       /* Set IP send function to send_ipv4() */
+       send_ip = &send_ipv4;
+}
+
+/**
+ * IPv4: Set the own IPv4 address.
+ *
+ * @param  _own_ip  client IPv4 address (e.g. 127.0.0.1)
+ */
+void set_ipv4_address(uint32_t _own_ip)
+{
+       own_ip = _own_ip;
+       ipv4_init();
+}
+
+/**
+ * IPv4: Get the own IPv4 address.
+ *
+ * @return client IPv4 address (e.g. 127.0.0.1)
+ */
+uint32_t get_ipv4_address(void)
+{
+       return own_ip;
+}
+
+/**
+ * IPv4: Set the IPv4 multicast address.
+ *
+ * @param  _own_ip  multicast IPv4 address (224.0.0.0 - 239.255.255.255)
+ */
+void set_ipv4_multicast(uint32_t _multicast_ip)
+{
+       // is this IP Multicast out of range (224.0.0.0 - 239.255.255.255)
+       if((htonl(_multicast_ip) < 0xE0000000)
+       || (htonl(_multicast_ip) > 0xEFFFFFFF)) {
+               multicast_ip = 0;
+               memset(multicast_mac, 0xFF, 6);
+               return;
+       }
+
+       multicast_ip = _multicast_ip;
+       multicast_mac[0] = 0x01;
+       multicast_mac[1] = 0x00;
+       multicast_mac[2] = 0x5E;
+       multicast_mac[3] = (uint8_t) 0x7F & (multicast_ip >> 16);
+       multicast_mac[4] = (uint8_t) 0xFF & (multicast_ip >>  8);
+       multicast_mac[5] = (uint8_t) 0xFF & (multicast_ip >>  0);
+}
+
+/**
+ * IPv4: Get the IPv4 multicast address.
+ *
+ * @return multicast IPv4 address (224.0.0.0 - 239.255.255.255 or 0 if not set)
+ */
+uint32_t get_ipv4_multicast(void)
+{
+       return multicast_ip;
+}
+
+/**
+ * IPv4: Set the routers IPv4 address.
+ *
+ * @param  _router_ip   router IPv4 address
+ */
+void set_ipv4_router(uint32_t _router_ip)
+{
+       router_ip = _router_ip;
+       ipv4_init();
+}
+
+/**
+ * IPv4: Get the routers IPv4 address.
+ *
+ * @return router IPv4 address
+ */
+uint32_t get_ipv4_router(void)
+{
+       return router_ip;
+}
+
+/**
+ * IPv4: Set the subnet mask.
+ *
+ * @param  _subnet_mask   netmask of the own IPv4 address
+ */
+void set_ipv4_netmask(uint32_t _subnet_mask)
+{
+       subnet_mask = _subnet_mask;
+       ipv4_init();
+}
+
+/**
+ * IPv4: Get the subnet mask.
+ *
+ * @return netmask of the own IPv4 address
+ */
+uint32_t get_ipv4_netmask(void)
+{
+       return subnet_mask;
+}
+
+/**
+ * IPv4: Get the default subnet mask according to the IP class
+ *
+ * @param ip_addr IPv4 address
+ * @return default netmask according to the IP class
+ */
+uint32_t get_default_ipv4_netmask(char *ip_addr)
+{
+       unsigned char top;
+
+       top = ip_addr[0];
+       if (top > 0 && top < 128)
+               return 0xFF000000; /* Class A: 255.0.0.0 */
+       else if (top >= 128 && top < 192)
+               return 0xFFFF0000; /* Class B: 255.255.0.0 */
+       else if (top >= 192 && top < 224)
+               return 0xFFFFFF00; /* Class C: 255.255.255.0 */
+       else
+               return 0;
+}
+
+/**
+ * IPv4: Creates IP-packet. Places IP-header in a packet and fills it
+ *       with corresponding information.
+ *       <p>
+ *       Use this function with similar functions for other network layers
+ *       (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param  packet      Points to the place where IP-header must be placed.
+ * @param  packetsize  Size of the packet in bytes incl. this hdr and data.
+ * @param  ip_proto    Type of the next level protocol (e.g. UDP).
+ * @param  ip_src      Sender IP address
+ * @param  ip_dst      Receiver IP address
+ * @see                iphdr
+ * @see                fill_ethhdr
+ * @see                fill_udphdr
+ * @see                fill_dnshdr
+ * @see                fill_btphdr
+ */
+void fill_iphdr(uint8_t * packet, uint16_t packetsize,
+           uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst)
+{
+       struct iphdr * iph = (struct iphdr *) packet;
+
+       iph -> ip_hlv = 0x45;
+       iph -> ip_tos = 0x10;
+       iph -> ip_len = htons(packetsize);
+       iph -> ip_id = htons(0);
+       iph -> ip_off = 0;
+       iph -> ip_ttl = 0xFF;
+       iph -> ip_p = ip_proto;
+       iph -> ip_src = htonl(ip_src);
+       iph -> ip_dst = htonl(ip_dst);
+       iph -> ip_sum = 0;
+}
+
+/**
+ * IPv4: Handles IPv4-packets according to Receive-handle diagram.
+ *
+ * @param  fd         socket fd
+ * @param  ip_packet  IP-packet to be handled
+ * @param  packetsize Length of the packet
+ * @return            ZERO - packet handled successfully;
+ *                    NON ZERO - packet was not handled (e.g. bad format)
+ * @see               receive_ether
+ * @see               iphdr
+ */
+int8_t handle_ipv4(int fd, uint8_t * ip_packet, uint32_t packetsize)
+{
+       struct iphdr * iph;
+       int32_t old_sum;
+       static uint8_t ip_heap[65536 + ETH_MTU_SIZE];
+
+       if (packetsize < sizeof(struct iphdr))
+               return -1; // packet is too small
+
+       iph = (struct iphdr * ) ip_packet;
+
+       /* Drop it if destination IPv4 address is no IPv4 Broadcast, no
+        * registered IPv4 Multicast and not our Unicast address
+        */
+       if((multicast_ip == 0 && iph->ip_dst >= 0xE0000000 && iph->ip_dst <= 
0xEFFFFFFF)
+       || (multicast_ip != iph->ip_dst && iph->ip_dst != 0xFFFFFFFF &&
+           own_ip != 0 && iph->ip_dst != own_ip)) {
+               return -1;
+       }
+
+       old_sum = iph -> ip_sum;
+       iph -> ip_sum = 0;
+       if (old_sum != checksum((uint16_t *) iph, sizeof (struct iphdr) >> 1))
+               return -1; // Wrong IP checksum
+
+       // is it the first fragment in a packet?
+       if (((iph -> ip_off) & 0x1FFF) == 0) {
+               // is it part of more fragments?
+               if (((iph -> ip_off) & 0x2000) == 0x2000) {
+                       memcpy(ip_heap, ip_packet, iph->ip_len);
+                       return 0;
+               }
+       }
+       // it's not the first fragment
+       else {
+               // get the first fragment
+               struct iphdr * iph_first = (struct iphdr * ) ip_heap;
+
+               // is this fragment not part of the first one, then exit
+               if ((iph_first->ip_id  != iph->ip_id ) ||
+                   (iph_first->ip_p   != iph->ip_p  ) ||
+                   (iph_first->ip_src != iph->ip_src) ||
+                   (iph_first->ip_dst != iph->ip_dst)) {
+                       return 0;
+               }
+
+               // this fragment is part of the first one!
+               memcpy(ip_heap + sizeof(struct iphdr) +
+                      ((iph -> ip_off) & 0x1FFF) * 8,
+                      ip_packet + sizeof(struct iphdr),
+                      iph -> ip_len - sizeof(struct iphdr));
+
+               // is it part of more fragments? Then return.
+               if (((iph -> ip_off) & 0x2000) == 0x2000) {
+                       return 0;
+               }
+
+               // packet is completly reassambled now!
+
+               // recalculate ip_len and set iph and ip_packet to the
+               iph_first->ip_len = iph->ip_len + ((iph->ip_off) & 0x1FFF) * 8;
+
+               // set iph and ip_packet to the resulting packet.
+               ip_packet = ip_heap;
+               iph = (struct iphdr * ) ip_packet;
+       }
+
+       switch (iph -> ip_p) {
+       case IPTYPE_ICMP:
+               return handle_icmp(fd, iph, ip_packet + sizeof(struct iphdr),
+                                  iph -> ip_len - sizeof(struct iphdr));
+       case IPTYPE_UDP:
+               return handle_udp(fd, ip_packet + sizeof(struct iphdr),
+                                 iph -> ip_len - sizeof(struct iphdr));
+       case IPTYPE_TCP:
+               return handle_tcp(ip_packet + sizeof(struct iphdr),
+                                 iph -> ip_len - sizeof(struct iphdr));
+       default:
+               break;
+       }
+       return -1; // Unknown protocol
+}
+
+/**
+ * IPv4: Send IPv4-packets.
+ *
+ *       Before the packet is sent there are some patcches performed:
+ *       - IPv4 source address is replaced by our unicast IPV4 address
+ *         if it is set to 0 or 1
+ *       - IPv4 destination address is replaced by our multicast IPV4 address
+ *         if it is set to 1
+ *       - IPv4 checksum is calculaded.
+ *       - If payload type is UDP, then the UDP checksum is calculated also.
+ *
+ *       We send an ARP request first, if this is the first packet sent to
+ *       the declared IPv4 destination address. In this case we store the
+ *       the packet and send it later if we receive the ARP response.
+ *       If the MAC address is known already, then we send the packet 
immediately.
+ *       If there is already an ARP request pending, then we drop this packet
+ *       and send again an ARP request.
+ *
+ * @param  fd         socket fd
+ * @param  ip_packet  IP-packet to be handled
+ * @param  packetsize Length of the packet
+ * @return            -2 - packet dropped (MAC address not resolved - ARP 
request pending)
+ *                    -1 - packet dropped (bad format)
+ *                     0 - packet stored  (ARP request sent - packet will be 
sent if
+ *                                         ARP response is received)
+ *                    >0 - packet send    (number of transmitted bytes is 
returned)
+ *
+ * @see               receive_ether
+ * @see               iphdr
+ */
+int send_ipv4(int fd, void* buffer, int len)
+{
+       arp_entry_t *arp_entry = 0;
+       struct iphdr *ip;
+       const uint8_t *mac_addr = 0;
+       uint32_t ip_dst = 0;
+
+       if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE)
+               return -1;
+
+       ip = (struct iphdr  *) buffer;
+
+       /* Replace source IPv4 address with our own unicast IPv4 address
+        * if it's 0 (= own unicast source address not specified).
+        */
+       if(ip->ip_src == 0) {
+               ip->ip_src = htonl( own_ip );
+       }
+       /* Replace source IPv4 address with our unicast IPv4 address and
+        * replace destination IPv4 address with our multicast IPv4 address
+        * if source address is set to 1.
+        */
+       else if(ip->ip_src == 1) {
+               ip->ip_src = htonl( own_ip );
+               ip->ip_dst = htonl( multicast_ip );
+       }
+
+       // Calculate the IPv4 checksum
+       ip->ip_sum = 0;
+       ip->ip_sum = checksum((uint16_t *) ip, sizeof (struct iphdr) >> 1);
+
+       // if payload type is UDP, then we need to calculate the
+       // UDP checksum that depends on the IP header
+       if(ip->ip_p == IPTYPE_UDP) {
+               fill_udp_checksum(ip);
+       }
+
+       ip_dst = ip->ip_dst;
+       // Check if the MAC address is already cached
+       if(~ip->ip_dst == 0
+       || ( ((~subnet_mask) & ip->ip_dst) == ~subnet_mask &&
+            (  subnet_mask  & ip->ip_dst) == (subnet_mask & own_ip)))  {
+               arp_entry = &arp_table[arp_producer];
+               mac_addr = broadcast_mac;
+       }
+       else if(ip->ip_dst == multicast_ip) {
+               arp_entry = &arp_table[arp_producer];
+               mac_addr = multicast_mac;
+       }
+       else {
+               // Check if IP address is in the same subnet as we are
+               if((subnet_mask & own_ip) == (subnet_mask & ip->ip_dst))
+                       arp_entry = lookup_mac_addr(ip->ip_dst);
+               // if not then we need to know the router's IP address
+               else {
+                       ip_dst = router_ip;
+                       arp_entry = lookup_mac_addr(router_ip);
+               }
+               if(arp_entry && memcmp(arp_entry->mac_addr, null_mac_addr, 6) 
!= 0)
+                       mac_addr = arp_entry->mac_addr;
+       }
+
+       // If we could not resolv the MAC address by our own...
+       if(!mac_addr) {
+               // send the ARP request
+               arp_send_request(fd, ip_dst);
+
+               // drop the current packet if there is already a ARP request 
pending
+               if(arp_entry)
+                       return -2;
+
+               // take the next entry in the ARP table to prepare a the new 
ARP entry.
+               arp_entry = &arp_table[arp_producer];
+               arp_producer = (arp_producer+1)%ARP_ENTRIES;
+
+               // if ARP table is full then we must drop the oldes entry.
+               if(arp_consumer == arp_producer)
+                       arp_consumer = (arp_consumer+1)%ARP_ENTRIES;
+
+               // store the packet to be send if the ARP reply is received
+               arp_entry->pkt_pending = 1;
+               arp_entry->ipv4_addr = ip_dst;
+               memset(arp_entry->mac_addr, 0, 6);
+               fill_ethhdr (pending_pkt_frame, htons(ETHERTYPE_IP),
+                            get_mac_address(), null_mac_addr);
+               memcpy(&pending_pkt_frame[sizeof(struct ethhdr)],
+                      buffer, len);
+               pending_pkt_len = len + sizeof(struct ethhdr);
+
+               set_timer(TICKS_SEC);
+               do {
+                       receive_ether(fd);
+                       if (!arp_entry->eth_len)
+                               break;
+               } while (get_timer() > 0);
+
+               return 0;
+       }
+
+       // Send the packet with the known MAC address
+       fill_ethhdr(arp_entry->eth_frame, htons(ETHERTYPE_IP),
+                   get_mac_address(), mac_addr);
+       memcpy(&arp_entry->eth_frame[sizeof(struct ethhdr)], buffer, len);
+       return send_ether(fd, arp_entry->eth_frame, len + sizeof(struct 
ethhdr));
+}
+
+/**
+ * IPv4: Calculate UDP checksum. Places the result into the UDP-header.
+ *      <p>
+ *      Use this function after filling the UDP payload.
+ *
+ * @param  ipv4_hdr    Points to the place where IPv4-header starts.
+ */
+static void fill_udp_checksum(struct iphdr *ipv4_hdr)
+{
+       unsigned i;
+       unsigned long checksum = 0;
+       struct iphdr ip_hdr;
+       char *ptr;
+       udp_hdr_t *udp_hdr;
+
+       udp_hdr = (udp_hdr_t *) (ipv4_hdr + 1);
+       udp_hdr->uh_sum = 0;
+
+       memset(&ip_hdr, 0, sizeof(struct iphdr));
+       ip_hdr.ip_src    = ipv4_hdr->ip_src;
+       ip_hdr.ip_dst    = ipv4_hdr->ip_dst;
+       ip_hdr.ip_len    = udp_hdr->uh_ulen;
+       ip_hdr.ip_p      = ipv4_hdr->ip_p;
+
+       ptr = (char*) udp_hdr;
+       for (i = 0; i < udp_hdr->uh_ulen; i+=2)
+               checksum += *((uint16_t*) &ptr[i]);
+
+       ptr = (char*) &ip_hdr;
+       for (i = 0; i < sizeof(struct iphdr); i+=2)
+               checksum += *((uint16_t*) &ptr[i]);
+
+       checksum = (checksum >> 16) + (checksum & 0xffff);
+       checksum += (checksum >> 16);
+       udp_hdr->uh_sum = ~checksum;
+
+       /* As per RFC 768, if the computed  checksum  is zero,
+        * it is transmitted as all ones (the equivalent in
+        * one's complement arithmetic).
+        */
+       if (udp_hdr->uh_sum == 0)
+               udp_hdr->uh_sum = ~udp_hdr->uh_sum;
+}
+
+/**
+ * IPv4: Calculates checksum for IP header.
+ *
+ * @param  packet     Points to the IP-header
+ * @param  words      Size of the packet in words incl. IP-header and data.
+ * @return            Checksum
+ * @see               iphdr
+ */
+static unsigned short checksum(unsigned short * packet, int words)
+{
+       unsigned long checksum;
+
+       for (checksum = 0; words > 0; words--)
+               checksum += *packet++;
+       checksum = (checksum >> 16) + (checksum & 0xffff);
+       checksum += (checksum >> 16);
+
+       return ~checksum;
+}
+
+static arp_entry_t* lookup_mac_addr(uint32_t ipv4_addr)
+{
+       unsigned int i;
+
+       for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) ) {
+               if(arp_table[i].ipv4_addr == ipv4_addr)
+                       return &arp_table[i];
+       }
+       return 0;
+}
+
+
+/**
+ * ARP: Sends an ARP-request package.
+ *      For given IPv4 retrieves MAC via ARP (makes several attempts)
+ *
+ * @param  fd        socket fd
+ * @param  dest_ip   IP of the host which MAC should be obtained
+ */
+static void arp_send_request(int fd, uint32_t dest_ip)
+{
+       arp_entry_t *arp_entry = &arp_table[arp_producer];
+
+       memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct 
arphdr));
+       fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REQUEST,
+                   get_mac_address(), own_ip, broadcast_mac, dest_ip);
+       fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP,
+                   get_mac_address(), broadcast_mac);
+
+       send_ether(fd, arp_entry->eth_frame,
+            sizeof(struct ethhdr) + sizeof(struct arphdr));
+}
+
+/**
+ * ARP: Sends an ARP-reply package.
+ *      This package is used to serve foreign requests (in case IP in
+ *      foreign request matches our host IP).
+ *
+ * @param  fd        socket fd
+ * @param  src_ip    requester IP address (foreign IP)
+ * @param  src_mac   requester MAC address (foreign MAC)
+ */
+static void arp_send_reply(int fd, uint32_t src_ip, uint8_t * src_mac)
+{
+       arp_entry_t *arp_entry = &arp_table[arp_producer];
+
+       memset(arp_entry->eth_frame, 0, sizeof(struct ethhdr) + sizeof(struct 
arphdr));
+       fill_ethhdr(arp_entry->eth_frame, ETHERTYPE_ARP,
+                   get_mac_address(), src_mac);
+       fill_arphdr(&arp_entry->eth_frame[sizeof(struct ethhdr)], ARP_REPLY,
+                   get_mac_address(), own_ip, src_mac, src_ip);
+
+       send_ether(fd, arp_entry->eth_frame,
+            sizeof(struct ethhdr) + sizeof(struct arphdr));
+}
+
+/**
+ * ARP: Creates ARP package. Places ARP-header in a packet and fills it
+ *      with corresponding information.
+ *      <p>
+ *      Use this function with similar functions for other network layers
+ *      (fill_ethhdr).
+ *
+ * @param  packet      Points to the place where ARP-header must be placed.
+ * @param  opcode      Identifies is it request (ARP_REQUEST)
+ *                     or reply (ARP_REPLY) package.
+ * @param  src_mac     sender MAC address
+ * @param  src_ip      sender IP address
+ * @param  dest_mac    receiver MAC address
+ * @param  dest_ip     receiver IP address
+ * @see                arphdr
+ * @see                fill_ethhdr
+ */
+static void fill_arphdr(uint8_t * packet, uint8_t opcode,
+                        const uint8_t * src_mac, uint32_t src_ip,
+                        const uint8_t * dest_mac, uint32_t dest_ip)
+{
+       struct arphdr * arph = (struct arphdr *) packet;
+
+       arph -> hw_type = htons(1);
+       arph -> proto_type = htons(ETHERTYPE_IP);
+       arph -> hw_len = 6;
+       arph -> proto_len = 4;
+       arph -> opcode = htons(opcode);
+
+       memcpy(arph->src_mac, src_mac, 6);
+       arph->src_ip = htonl(src_ip);
+       memcpy(arph->dest_mac, dest_mac, 6);
+       arph->dest_ip = htonl(dest_ip);
+}
+
+/**
+ * ARP: Handles ARP-messages according to Receive-handle diagram.
+ *      Updates arp_table for outstanding ARP requests (see arp_getmac).
+ *
+ * @param  fd         socket fd
+ * @param  packet     ARP-packet to be handled
+ * @param  packetsize length of the packet
+ * @return            ZERO - packet handled successfully;
+ *                    NON ZERO - packet was not handled (e.g. bad format)
+ * @see               arp_getmac
+ * @see               receive_ether
+ * @see               arphdr
+ */
+int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize)
+{
+       struct arphdr * arph = (struct arphdr *) packet;
+
+       if (packetsize < sizeof(struct arphdr))
+               return -1; // Packet is too small
+
+       if (arph -> hw_type != htons(1) || arph -> proto_type != 
htons(ETHERTYPE_IP))
+               return -1; // Unknown hardware or unsupported protocol
+
+       if (arph -> dest_ip != htonl(own_ip))
+               return -1; // receiver IP doesn't match our IP
+
+       switch(htons(arph -> opcode)) {
+       case ARP_REQUEST:
+               // foreign request
+               if(own_ip != 0)
+                       arp_send_reply(fd, htonl(arph->src_ip), arph -> 
src_mac);
+               return 0; // no error
+       case ARP_REPLY: {
+               unsigned int i;
+               // if it is not for us -> return immediately
+               if(memcmp(get_mac_address(), arph->dest_mac, 6)) {
+                       return 0; // no error
+               }
+
+               if(arph->src_ip == 0) {
+                       // we are not interested for a MAC address if
+                       // the IPv4 address is 0.0.0.0 or ff.ff.ff.ff
+                       return -1;
+               }
+
+               // now let's find the corresponding entry in the ARP table
+
+               for(i=arp_consumer; i != arp_producer; i = ((i+1)%ARP_ENTRIES) 
) {
+                       if(arp_table[i].ipv4_addr == arph->src_ip)
+                               break;
+               }
+               if(i == arp_producer || memcmp(arp_table[i].mac_addr, 
null_mac_addr, 6) != 0) {
+                       // we have not asked to resolve this IPv4 address !
+                       return -1;
+               }
+
+               memcpy(arp_table[i].mac_addr, arph->src_mac, 6);
+
+               // do we have something to send
+               if (arp_table[i].pkt_pending) {
+                       struct ethhdr * ethh = (struct ethhdr *) 
pending_pkt_frame;
+                       memcpy(ethh -> dest_mac, arp_table[i].mac_addr, 6);
+
+                       send_ether(fd, pending_pkt_frame, pending_pkt_len);
+                       arp_table[i].pkt_pending = 0;
+                       arp_table[i].eth_len = 0;
+               }
+               return 0; // no error
+       }
+       default:
+               break;
+       }
+       return -1; // Invalid message type
+}
+
+/**
+ * ICMP: Send an ICMP Echo request to destination IPv4 address.
+ *       This function does also set a global variable to the
+ *       destination IPv4 address. If there is an ICMP Echo Reply
+ *       received later then the variable is set back to 0.
+ *       In other words, reading a value of 0 form this variable
+ *       means that an answer to the request has been arrived.
+ *
+ * @param  fd            socket descriptor
+ * @param  _ping_dst_ip  destination IPv4 address
+ */
+void ping_ipv4(int fd, uint32_t _ping_dst_ip)
+{
+       unsigned char packet[sizeof(struct iphdr) + sizeof(struct icmphdr)];
+       struct icmphdr *icmp;
+
+       ping_dst_ip = _ping_dst_ip;
+
+       if(ping_dst_ip == 0)
+               return;
+
+       fill_iphdr(packet, sizeof(struct iphdr) + sizeof(struct icmphdr), 
IPTYPE_ICMP,
+                  0, ping_dst_ip);
+       icmp = (struct icmphdr *) (packet + sizeof(struct iphdr));
+       icmp->type = ICMP_ECHO_REQUEST;
+       icmp->code = 0;
+       icmp->checksum = 0;
+       icmp->options.echo.id = 0xd476;
+       icmp->options.echo.seq = 1;
+
+       memset(icmp->payload.data, '*', sizeof(icmp->payload.data));
+
+       icmp->checksum =
+           checksum((unsigned short *) icmp, sizeof(struct icmphdr) >> 1);
+       send_ipv4(fd, packet, sizeof(struct iphdr) + sizeof(struct icmphdr));
+}
+
+/**
+ * ICMP: Return host IPv4 address that we are waiting for a
+ *       ICMP Echo reply message. If this value is 0 then we have
+ *       received an reply.
+ *
+ * @return  ping_dst_ip  host IPv4 address
+ */
+uint32_t pong_ipv4(void)
+{
+       return ping_dst_ip;
+}
+
+/**
+ * ICMP: Handles ICMP-packets according to Receive-handle diagram.
+ *
+ * @param  fd         socket fd
+ * @param  icmp_packet  ICMP-packet to be handled
+ * @param  packetsize   Length of the packet
+ * @return              ZERO - packet handled successfully;
+ *                      NON ZERO - packet was not handled (e.g. bad format)
+ * @see                 handle_ipv4
+ */
+static int8_t handle_icmp(int fd, struct iphdr * iph, uint8_t * packet,
+                         int32_t packetsize)
+{
+       struct icmphdr *icmp = (struct icmphdr *) packet;
+
+       switch(icmp->type) {
+       case ICMP_ECHO_REPLY:
+               if (icmp->options.echo.id != 0xd476)
+                       return -1;
+               if (icmp->options.echo.seq != 1)
+                       return -1;
+               if(ping_dst_ip != iph->ip_src
+               || ping_dst_ip == 0)
+                       return -1;
+               ping_dst_ip = 0;
+               break;
+       case ICMP_DST_UNREACHABLE: {
+               // We've got Destination Unreachable msg
+               // Inform corresponding upper network layers
+               struct iphdr * bad_iph = (struct iphdr * ) &icmp->payload;
+
+               switch(bad_iph->ip_p) {
+               case IPTYPE_TCP:
+                       handle_tcp_dun((uint8_t *) (bad_iph + 1), packetsize
+                                      - sizeof(struct icmphdr)
+                                      - sizeof(struct iphdr), icmp->code);
+                       break;
+               case IPTYPE_UDP:
+                       handle_udp_dun((uint8_t *) (bad_iph + 1), packetsize
+                                      - sizeof(struct icmphdr)
+                                      - sizeof(struct iphdr), icmp->code);
+                       break;
+               }
+               break;
+       }
+       case ICMP_SRC_QUENCH:
+               break;
+       case ICMP_REDIRECT:
+               break;
+       case ICMP_ECHO_REQUEST: {
+               // We've got an Echo Request - answer with Echo Replay msg
+               unsigned char reply_packet[sizeof(struct iphdr) + packetsize];
+               struct icmphdr *reply_icmph;
+
+               fill_iphdr(reply_packet, sizeof(struct iphdr) + packetsize,
+                          IPTYPE_ICMP, 0, iph->ip_src);
+
+               reply_icmph = (struct icmphdr *) &reply_packet[sizeof(struct 
iphdr)];
+               memcpy(reply_icmph, packet, packetsize);
+               reply_icmph -> type = ICMP_ECHO_REPLY;
+               reply_icmph -> checksum = 0;
+               reply_icmph->checksum = checksum((unsigned short *) reply_icmph,
+                                                sizeof(struct icmphdr) >> 1);
+
+               send_ipv4(fd, reply_packet, sizeof(struct iphdr) + packetsize);
+               break;
+       }
+       case ICMP_TIME_EXCEEDED:
+               break;
+       case ICMP_PARAMETER_PROBLEM:
+               break;
+       case ICMP_TIMESTAMP_REQUEST:
+               break;
+       case ICMP_TIMESTAMP_REPLY:
+               break;
+       case ICMP_INFORMATION_REQUEST:
+               break;
+       case ICMP_INFORMATION_REPLY:
+               break;
+       }
+       return 0;
+}
diff --git a/pc-bios/s390-ccw/libnet/ipv4.h b/pc-bios/s390-ccw/libnet/ipv4.h
new file mode 100644
index 0000000..5717c9a
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ipv4.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ * 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 _IPV4_H_
+#define _IPV4_H_
+
+#include <stdint.h>
+
+#define IPTYPE_ICMP         1
+
+/** \struct iphdr
+ *  A header for IP-packets.
+ *  For more information see RFC 791.
+ */
+struct iphdr {
+       uint8_t ip_hlv;      /**< Header length and version of the header      
*/
+       uint8_t ip_tos;      /**< Type of Service                              
*/
+       uint16_t ip_len;     /**< Length in octets, inlc. this header and data 
*/
+       uint16_t ip_id;      /**< ID is used to aid in assembling framents     
*/
+       uint16_t ip_off;     /**< Info about fragmentation (control, offset)   
*/
+       uint8_t ip_ttl;      /**< Time to Live                                 
*/
+       uint8_t ip_p;        /**< Next level protocol type                     
*/
+       uint16_t ip_sum;     /**< Header checksum                              
*/
+       uint32_t ip_src;     /**< Source IP address                            
*/
+       uint32_t ip_dst;     /**< Destination IP address                       
*/
+};
+typedef struct iphdr ipv4_hdr_t;
+
+/* ICMP Error Codes */
+#define ICMP_NET_UNREACHABLE 0
+#define ICMP_HOST_UNREACHABLE 1
+#define ICMP_PROTOCOL_UNREACHABLE 2
+#define ICMP_PORT_UNREACHABLE 3
+#define ICMP_FRAGMENTATION_NEEDED 4
+#define ICMP_SOURCE_ROUTE_FAILED 5
+
+/** \struct arphdr
+ *  A header for ARP-messages, retains info about HW and proto addresses.
+ *  For more information see RFC 826.
+ */
+struct arphdr {
+       uint16_t hw_type;    /**< HW address space (1 for Ethernet)            
*/
+       uint16_t proto_type; /**< Protocol address space                       
*/
+       uint8_t hw_len;      /**< Byte length of each HW address               
*/
+       uint8_t proto_len;   /**< Byte length of each proto address            
*/
+       uint16_t opcode;     /**< Identifies is it request (1) or reply (2)    
*/
+       uint8_t src_mac[6];  /**< HW address of sender of this packet          
*/
+       uint32_t src_ip;     /**< Proto address of sender of this packet       
*/
+       uint8_t dest_mac[6]; /**< HW address of target of this packet          
*/
+       uint32_t dest_ip;    /**< Proto address of target of this packet       
*/
+} __attribute((packed));
+
+/************** Initialization of the IPv4 network layer. **************/
+extern void     set_ipv4_address(uint32_t own_ip);
+extern uint32_t get_ipv4_address(void);
+extern void     set_ipv4_multicast(uint32_t multicast_ip);
+extern uint32_t get_ipv4_multicast(void);
+extern void     set_ipv4_router(uint32_t router_ip);
+extern uint32_t get_ipv4_router(void);
+extern void     set_ipv4_netmask(uint32_t subnet_mask);
+extern uint32_t get_ipv4_netmask(void);
+extern uint32_t get_default_ipv4_netmask(char *ip_addr);
+
+extern int   (*send_ip) (int fd, void *, int);
+
+/* fills ip header */
+extern void fill_iphdr(uint8_t * packet, uint16_t packetsize,
+                       uint8_t ip_proto, uint32_t ip_src, uint32_t ip_dst);
+
+/* Send a IPv4 packet. Adding the Ethernet-Header and resolving the
+ * MAC address is done transparent in the background if necessary.
+ */
+extern int send_ipv4(int fd, void* buffer, int len);
+
+/* Sends an ICMP Echo request to destination IPv4 address */
+extern void ping_ipv4(int fd, uint32_t _ping_dst_ip);
+
+/* Returns host IPv4 address that we are waiting for a response */
+extern uint32_t pong_ipv4(void);
+
+/* Handles IPv4-packets that are detected by receive_ether. */
+extern int8_t handle_ipv4(int fd, uint8_t * packet, uint32_t packetsize);
+
+/* Handles ARP-packets that are detected by receive_ether. */
+extern int8_t handle_arp(int fd, uint8_t * packet, uint32_t packetsize);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/ipv6.c b/pc-bios/s390-ccw/libnet/ipv6.c
new file mode 100644
index 0000000..62a444e
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ipv6.c
@@ -0,0 +1,774 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include "ethernet.h"
+#include "ipv6.h"
+#include "icmpv6.h"
+#include "ndp.h"
+#include "udp.h"
+
+#undef IPV6_DEBUG
+//#define IPV6_DEBUG
+#ifdef IPV6_DEBUG
+#define dprintf(_x ...) do { printf(_x); } while (0)
+#else
+#define dprintf(_x ...)
+#endif
+
+/****************************** PROTOTYPES *******************************/
+static void ipv6_init(int fd);
+static int ip6_is_multicast (ip6_addr_t * ip);
+
+/****************************** LOCAL VARIABLES **************************/
+
+/* List of Ipv6 Addresses */
+static struct ip6addr_list_entry *first_ip6;
+static struct ip6addr_list_entry *last_ip6;
+
+/* Own IPv6 address */
+static struct ip6addr_list_entry *own_ip6;
+
+/* All nodes link-local address */
+struct ip6addr_list_entry all_nodes_ll;
+
+/* Null IPv6 address */
+static ip6_addr_t null_ip6;
+
+/* helper variables */
+static uint8_t null_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+struct ip6_config ip6_state;
+
+/****************************** IMPLEMENTATION ***************************/
+
+/**
+ * IPv6: Set the own IPv6 address.
+ *
+ * @param  fd            Socket descriptor
+ * @param  _own_ip       client IPv6 address (e.g. ::1)
+ */
+void set_ipv6_address(int fd, ip6_addr_t *_own_ip6)
+{
+       struct ip6addr_list_entry *ile;
+
+       ile = malloc(sizeof(struct ip6addr_list_entry));
+       if (!ile)
+               return;
+       memset(ile, 0, sizeof(struct ip6addr_list_entry));
+       own_ip6 = ile;
+
+       /* If no address was passed as a parameter generate a link-local
+        * address from our MAC address.*/
+       if (_own_ip6 == NULL)
+               ip6_create_ll_address(get_mac_address(), &own_ip6->addr);
+       else
+               memcpy (&(own_ip6->addr.addr), _own_ip6, 16);
+
+       /* Add to our list of IPv6 addresses */
+       ip6addr_add (own_ip6);
+
+       ipv6_init(fd);
+
+       /*
+        * Check whether we've got a non-link-local address during
+        * ipv6_init() and use that as preferred address if possible
+        */
+       if (_own_ip6 == NULL) {
+               for (ile = first_ip6; ile != NULL ; ile = ile->next) {
+                       if (!ip6_is_multicast(&ile->addr) &&
+                           !ip6_is_linklocal(&ile->addr)) {
+                               own_ip6 = ile;
+                               break;
+                       }
+               }
+       }
+}
+
+/**
+ * IPv6: Get pointer to own IPv6 address.
+ *
+ * @return pointer to client IPv6 address (e.g. ::1)
+ */
+ip6_addr_t *get_ipv6_address(void)
+{
+       return (ip6_addr_t *) &(own_ip6->addr);
+}
+
+/**
+ * IPv6: Search for IPv6 address in list
+ *
+ * @return 0 - IPv6 address is not in list
+ *         1 - IPv6 address is in list
+ */
+static int8_t find_ip6addr(ip6_addr_t *ip)
+{
+       struct ip6addr_list_entry *n = NULL;
+
+       if (ip == NULL)
+           return 0;
+
+       for (n = first_ip6; n != NULL ; n=n->next)
+               if (ip6_cmp (&(n->addr), ip))
+                       return 1; /* IPv6 address is in  our list*/
+
+       return 0; /* not one of our IPv6 addresses*/
+}
+
+/**
+ * NET: Handles IPv6-packets
+ *
+ * @param  fd         - Socket descriptor
+ * @param  ip6_packet - Pointer to IPv6 header
+ * @param  packetsize - Size of Ipv6 packet
+ * @return ERROR      - -1 if packet is too small or unknown protocol
+ *                     return value of handle_udp
+ *
+ * @see handle_udp
+ * @see ip6hdr
+ */
+int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize)
+{
+
+       struct ip6hdr *ip6 = NULL;
+       ip6 = (struct ip6hdr *) ip6_packet;
+
+       /* Only handle packets which are for us */
+       if (! find_ip6addr(&(ip6->dst)))
+               return -1;
+
+       if (packetsize < sizeof(struct ip6hdr))
+               return -1; // packet is too small
+
+       switch (ip6->nh) {
+               case IPTYPE_UDP:
+                       return handle_udp (fd, ip6_packet + sizeof (struct 
ip6hdr),
+                                       ip6->pl);
+               case IPTYPE_ICMPV6:
+                       return handle_icmpv6 (fd, (struct ethhdr *) ip6_packet 
- sizeof(struct ethhdr),
+                                             ip6_packet);
+       }
+
+       return -1; // unknown protocol
+}
+
+ /**
+ * NET: Creates IPv6-packet. Places IPv6-header in a packet and fills it
+ *      with corresponding information.
+ *      <p>
+ *      Use this function with similar functions for other network layers
+ *      (fill_ethhdr, fill_udphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param  packet      Points to the place where IPv6-header must be placed.
+ * @param  packetsize  Size of payload (i.e. excluding ethhdr and ip6hdr)
+ * @param  ip_proto    Type of the next level protocol (e.g. UDP).
+ * @param  ip6_src     Sender IPv6 address
+ * @param  ip6_dst     Receiver IPv6 address
+ * @see                ip6hdr
+ * @see                fill_iphdr
+ * @see                fill_ethhdr
+ * @see                fill_udphdr
+ * @see                fill_dnshdr
+ * @see                fill_btphdr
+ */
+void fill_ip6hdr(uint8_t * packet, uint16_t packetsize,
+                uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst)
+{
+       struct ip6hdr * ip6h = (struct ip6hdr *) packet;
+
+       ip6h->ver_tc_fl = 6 << 28;      // set version to 6
+       ip6h->pl = packetsize;          // IPv6 payload size
+       ip6h->nh = ip_proto;
+       ip6h->hl = 255;
+       memcpy (&(ip6h->src), ip6_src, IPV6_ADDR_LENGTH);
+       memcpy (&(ip6h->dst), ip6_dst, IPV6_ADDR_LENGTH);
+}
+
+/**
+ * NET: For a given MAC calculates EUI64-Identifier.
+ *      See RFC 4291 "IP Version 6 Addressing Architecture"
+ *
+ */
+uint64_t mac2eui64(const uint8_t *mac)
+{
+       uint8_t eui64id[8];
+       uint64_t retid;
+
+       memcpy (eui64id, mac, 3);
+       memcpy (eui64id + 5, mac + 3, 3);
+       eui64id[3] = 0xff;
+       eui64id[4] = 0xfe;
+
+       memcpy(&retid, eui64id, 8);
+       return retid;
+}
+
+/**
+ * NET: create link-local IPv6 address
+ *
+ * @param  own_mac    MAC of NIC
+ * @param ll_addr     pointer to link-local address which should be created
+ */
+void ip6_create_ll_address(const uint8_t *own_mac, ip6_addr_t *ll_addr)
+{
+       ll_addr->part.prefix = IPV6_LL_PREFIX;
+       ll_addr->part.interface_id = mac2eui64((uint8_t *) own_mac);
+}
+
+/*
+ * NET: check if we already have an address with the same prefix.
+ * @param  struct ip6_addr_list_entry *ip6
+ * @return true or false
+ */
+int8_t unknown_prefix(ip6_addr_t *ip)
+{
+       struct ip6addr_list_entry *node;
+
+       for( node = first_ip6; node != NULL; node=node->next )
+               if( node->addr.part.prefix == ip->part.prefix )
+                       return 0; /* address is one of ours */
+
+       return 1; /* prefix not yet in our list */
+}
+
+/*
+ * NET: Create empty element for prefix list and return a pointer to it;
+ * @return NULL - malloc failed
+ *        ! NULL - pointer to new prefix_info
+ */
+struct prefix_info *ip6_create_prefix_info(void)
+{
+       struct prefix_info *prfx_info;
+
+       prfx_info = malloc (sizeof(struct prefix_info));
+       if (!prfx_info)
+               return NULL;
+       memset(prfx_info, 0, sizeof(struct prefix_info));
+
+       return prfx_info;
+}
+
+/*
+ * NET: create a new IPv6 address with a given network prefix
+ *     and add it to our IPv6 address list
+ *
+ * @param  ip6_addr prefix (as received in RA)
+ * @return NULL - pointer to new ip6addr_list entry
+ */
+void *ip6_prefix2addr(ip6_addr_t prefix)
+{
+       struct ip6addr_list_entry *new_address;
+       uint64_t interface_id;
+
+       new_address = malloc (sizeof(struct ip6addr_list_entry));
+       if( !new_address )
+               return NULL;
+       memset(new_address, 0, sizeof(struct ip6addr_list_entry));
+
+       /* fill new addr struct */
+       /* extract prefix from Router Advertisement */
+       memcpy (&(new_address->addr.part.prefix), &prefix, 8 );
+
+       /* interface id is generated from MAC address */
+       interface_id = mac2eui64 (get_mac_address());
+       memcpy (&(new_address->addr.part.interface_id), &interface_id, 8);
+
+       return new_address;
+}
+
+/**
+ * NET: add new IPv6 adress to list
+ *
+ * @param   ip6_addr *new_address
+ * @return  0 - passed pointer = NULL;
+ *         1 - ok
+ */
+int8_t ip6addr_add(struct ip6addr_list_entry *new_address)
+{
+       struct ip6addr_list_entry *solicited_node;
+
+
+       if (new_address == NULL)
+               return 0;
+
+        /* Don't add the same address twice */
+       if (find_ip6addr (&(new_address->addr)))
+               return 0;
+
+       /* If address is a unicast address, we also have to process packets
+        * for its solicited-node multicast address.
+        * See RFC 2373 - IP Version 6 Adressing Architecture */
+       if (! ip6_is_multicast(&(new_address->addr))) {
+               solicited_node = malloc(sizeof(struct ip6addr_list_entry));
+               if (! solicited_node)
+                       return 0;
+               memset(solicited_node, 0, sizeof(struct ip6addr_list_entry));
+
+               solicited_node->addr.part.prefix       = IPV6_SOLIC_NODE_PREFIX;
+               solicited_node->addr.part.interface_id = 
IPV6_SOLIC_NODE_IFACE_ID;
+               solicited_node->addr.addr[13] = new_address->addr.addr[13];
+               solicited_node->addr.addr[14] = new_address->addr.addr[14];
+               solicited_node->addr.addr[15] = new_address->addr.addr[15];
+               ip6addr_add (solicited_node);
+       }
+
+       if (first_ip6 == NULL)
+               first_ip6 = new_address;
+       else
+               last_ip6->next = new_address;
+       last_ip6 = new_address;
+       last_ip6->next = NULL;
+
+       return 1; /* no error */
+}
+
+/**
+ * NET: Initialize IPv6
+ *
+ * @param  fd            socket fd
+ */
+static void ipv6_init(int fd)
+{
+       int i = 0;
+
+       send_ip = &send_ipv6;
+
+       /* Address configuration parameters */
+       ip6_state.managed_mode = 0;
+
+       /* Null IPv6 address */
+       null_ip6.part.prefix       = 0;
+       null_ip6.part.interface_id = 0;
+
+       /* Multicast addresses */
+       all_nodes_ll.addr.part.prefix         = 0xff02000000000000;
+       all_nodes_ll.addr.part.interface_id   = 1;
+       ip6addr_add(&all_nodes_ll);
+
+       ndp_init();
+
+       send_router_solicitation (fd);
+       for(i=0; i < 4 && !is_ra_received(); i++) {
+               set_timer(TICKS_SEC);
+               do {
+                       receive_ether(fd);
+                       if (is_ra_received())
+                               break;
+               } while (get_timer() > 0);
+       }
+}
+
+/**
+ * NET: compare IPv6 adresses
+ *
+ * @param  ip6_addr ip_1
+ * @param  ip6_addr ip_2
+ */
+int8_t ip6_cmp(ip6_addr_t *ip_1, ip6_addr_t *ip_2)
+{
+       return ((int8_t) !memcmp( &(ip_1->addr[0]), &(ip_2->addr[0]),
+               IPV6_ADDR_LENGTH ));
+}
+
+/**
+ * NET: Calculate checksum over IPv6 header and upper-layer protocol
+ *      (e.g. UDP or ICMPv6)
+ *
+ * @param  *ip    - pointer to IPv6 address
+ * @return true or false
+ */
+int ip6_is_multicast(ip6_addr_t * ip)
+{
+       return ip->addr[0] == 0xFF;
+}
+
+/**
+ * NET: Generate multicast MAC address from IPv6 address
+ *      (e.g. UDP or ICMPv6)
+ *
+ * @param  *ip    - pointer to IPv6 address
+ * @param  *mc_mac  pointer to an array with 6 bytes (for the MAC address)
+ * @return pointer to Multicast MAC address
+ */
+static uint8_t *ip6_to_multicast_mac(ip6_addr_t * ip, uint8_t *mc_mac)
+{
+       mc_mac[0] = 0x33;
+       mc_mac[1] = 0x33;
+       memcpy (mc_mac+2, (uint8_t *) &(ip->addr)+12, 4);
+
+       return mc_mac;
+}
+
+/**
+ * Check whether an IPv6 address is on the same network as we are
+ */
+static bool is_ip6addr_in_my_net(ip6_addr_t *ip)
+{
+       struct ip6addr_list_entry *n = NULL;
+
+       for (n = first_ip6; n != NULL; n = n->next) {
+               if (n->addr.part.prefix == ip->part.prefix)
+                       return true;  /* IPv6 address is in our neighborhood */
+       }
+
+       return false;    /* not in our neighborhood */
+}
+
+/**
+ * NET: calculate checksum over IPv6 header and upper-layer protocol
+ *      (e.g. UDP or ICMPv6)
+ *
+ * @param  struct ip6hdr *ip6h    - pointer to IPv6 header
+ * @param  unsigned short *packet - pointer to header of upper-layer
+ *                                 protocol
+ * @param  int words              - number of words (as in 2 bytes)
+ *                                 starting from *packet
+ * @return checksum
+ */
+static unsigned short ip6_checksum(struct ip6hdr *ip6h, unsigned short *packet,
+                                  int words)
+{
+       int i=0;
+       unsigned long checksum;
+       struct ip6hdr pseudo_ip6h;
+       unsigned short *pip6h;
+
+       memcpy (&pseudo_ip6h, ip6h, sizeof(struct ip6hdr));
+       pseudo_ip6h.hl        = ip6h->nh;
+       pseudo_ip6h.ver_tc_fl = 0;
+       pseudo_ip6h.nh        = 0;
+       pip6h = (unsigned short *) &pseudo_ip6h;
+
+       for (checksum = 0; words > 0; words--) {
+               checksum += *packet++;
+               i++;
+       }
+
+       for (i = 0; i < 20; i++) {
+               checksum += *pip6h++;
+       }
+
+       checksum = (checksum >> 16) + (checksum & 0xffff);
+       checksum += (checksum >> 16);
+
+       return ~checksum;
+}
+
+/**
+ * NET: Handles IPv6-packets
+ *
+ * @param fd          socket fd
+ * @param ip6_packet  Pointer to IPv6 header in packet
+ * @param packetsize  Size of IPv6 packet
+ * @return -1 : Some error occured
+ *          0 : packet stored (NDP request sent - packet will be sent if
+ *                             NDP response is received)
+ *         >0 : packet sent   (number of transmitted bytes is returned)
+ *
+ * @see receive_ether
+ * @see ip6hdr
+ */
+int send_ipv6(int fd, void* buffer, int len)
+{
+       struct ip6hdr *ip6h;
+       struct udphdr *udph;
+       struct icmp6hdr *icmp6h;
+       ip6_addr_t ip_dst;
+       uint8_t *mac_addr, mc_mac[6];
+       static uint8_t ethframe[ETH_MTU_SIZE];
+
+       mac_addr = null_mac;
+
+       ip6h    = (struct ip6hdr *) buffer;
+       udph   = (struct udphdr *)   ((uint8_t *) ip6h + sizeof (struct 
ip6hdr));
+       icmp6h = (struct icmp6hdr *) ((uint8_t *) ip6h + sizeof (struct 
ip6hdr));
+
+       memcpy(&ip_dst, &ip6h->dst, 16);
+
+       if(len + sizeof(struct ethhdr) > ETH_MTU_SIZE)
+               return -1;
+
+       if ( ip6_cmp (&ip6h->src, &null_ip6))
+               memcpy (&(ip6h->src), get_ipv6_address(), IPV6_ADDR_LENGTH);
+
+       if (ip6h->nh == 17) {//UDP
+               udph->uh_sum = ip6_checksum (ip6h, (unsigned short *) udph ,
+                                            ip6h->pl >> 1);
+               /* As per RFC 768, if the computed  checksum  is zero,
+                * it is transmitted as all ones (the equivalent in
+                * one's complement arithmetic).
+                */
+               if (udph->uh_sum == 0)
+                       udph->uh_sum = ~udph->uh_sum;
+       }
+       else if (ip6h->nh == 0x3a) //ICMPv6
+               icmp6h->checksum = ip6_checksum (ip6h,
+                                                (unsigned short *) icmp6h,
+                                                ip6h->pl >> 1);
+
+       if (ip6_is_multicast (&ip_dst)) {
+               /* If multicast, then create a proper multicast mac address */
+               mac_addr = ip6_to_multicast_mac (&ip_dst, mc_mac);
+       } else if (!is_ip6addr_in_my_net(&ip_dst)) {
+               /* If server is not in same subnet, user MAC of the router */
+               struct router *gw;
+               gw = ipv6_get_default_router(&ip6h->src);
+               mac_addr = gw ? gw->mac : null_mac;
+       } else {
+               /* Normal unicast, so use neighbor cache to look up MAC */
+               struct neighbor *n = find_neighbor (&ip_dst);
+               if (n) {                                /* Already cached ? */
+                       if (memcmp(n->mac, null_mac, ETH_ALEN) != 0)
+                               mac_addr = n->mac;              /* found it */
+               } else {
+                       mac_addr = null_mac;
+                       n = malloc(sizeof(struct neighbor));
+                       if (!n)
+                               return -1;
+                       memset(n, 0, sizeof(struct neighbor));
+                       memcpy(&(n->ip.addr[0]), &ip_dst, 16);
+                       n->status = NB_PROBE;
+                       n->times_asked += 1;
+                       neighbor_add(n);
+               }
+
+               if (! memcmp (mac_addr, &null_mac, 6)) {
+                       if (n->eth_len == 0) {
+                               send_neighbour_solicitation (fd, &ip_dst);
+
+                               // Store the packet until we know the MAC 
address
+                               fill_ethhdr (n->eth_frame,
+                                            htons(ETHERTYPE_IPv6),
+                                            get_mac_address(),
+                                            mac_addr);
+                               memcpy (&(n->eth_frame[sizeof(struct ethhdr)]),
+                                      buffer, len);
+                               n->eth_len = len;
+                               set_timer(TICKS_SEC);
+                               do {
+                                       receive_ether(fd);
+                                       if (n->status == NB_REACHABLE)
+                                               return len;
+                               } while (get_timer() > 0);
+                               return 0;
+                       }
+               }
+       }
+
+       if (mac_addr == null_mac)
+               return -1;
+
+       fill_ethhdr(ethframe, htons(ETHERTYPE_IPv6), get_mac_address(),
+                   mac_addr);
+       memcpy(&ethframe[sizeof(struct ethhdr)], buffer, len);
+
+       return send_ether(fd, ethframe, len + sizeof(struct ethhdr));
+}
+
+static int check_colons(const char *str)
+{
+       char *pch, *prv;
+       int col = 0;
+       int dcol = 0;
+
+       dprintf("str : %s\n",str);
+       pch = strchr(str, ':');
+       while(pch != NULL){
+               prv = pch;
+               pch = strchr(pch+1, ':');
+               if((pch-prv) != 1) {
+                       col++;
+               } else {
+                       col--; /* Its part of double colon */
+                       dcol++;
+               }
+       }
+
+       dprintf("The number of  col : %d \n",col);
+       dprintf("The number of dcol : %d \n",dcol);
+
+       if((dcol > 1) ||                      /* Cannot have 2 "::" */
+          ((dcol == 1) && (col > 5)) ||      /* Too many ':'s */
+          ((dcol == 0) && (col != 7)) ) {    /* Too few ':'s */
+               dprintf(" exiting for check_colons \n");
+               return 0;
+       }
+
+       return (col+dcol);
+}
+
+static int ipv6str_to_bytes(const char *str, char *ip)
+{
+       char block[5];
+       int res;
+       char *pos;
+       uint32_t cnt = 0, len;
+
+       dprintf("str : %s \n",str);
+
+       while (*str != 0) {
+               if (cnt > 15 || !isxdigit(*str)){
+                       return 0;
+               }
+               if ((pos = strchr(str, ':')) != NULL) {
+                       len = (int16_t) (pos - str);
+                       dprintf("\t len  is : %d \n",len);
+                       if (len > 4)
+                               return 0;
+                       strncpy(block, str, len);
+                       block[len] = 0;
+                       dprintf("\t str   : %s \n",str);
+                       dprintf("\t block : %s \n",block);
+                       str += len;
+               } else {
+                       strncpy(block, str, 4);
+                       block[4] = 0;
+                       dprintf("\t str   : %s \n",str);
+                       dprintf("\t block : %s \n",block);
+                       str += strlen(block);
+               }
+               res = strtol(block, NULL, 16);
+               dprintf("\t res : %x \n",res);
+               if ((res > 0xFFFF) || (res < 0))
+                       return 0;
+               ip[cnt++] = (res & 0xFF00) >> 8;
+               ip[cnt++] = (res & 0x00FF);
+               if (*str == ':'){
+                       str++;
+               }
+       }
+
+       dprintf("cnt : %d\n",cnt);
+       return cnt;
+}
+
+int str_to_ipv6(const char *str, uint8_t *ip)
+{
+       int i, k;
+       uint16_t len;
+       char *ptr;
+       char tmp[30], buf[16];
+
+       memset(ip,0,16);
+
+       if(!check_colons(str))
+               return 0;
+
+       if ((ptr = strstr(str, "::")) != NULL) {
+               /* Handle the ::1 IPv6 loopback */
+               if(!strcmp(str,"::1")) {
+                       ip[15] = 1;
+                       return 16;
+               }
+               len = (ptr-str);
+               dprintf(" len : %d \n",len);
+               if (len >= sizeof(tmp))
+                       return 0;
+               strncpy(tmp, str, len);
+               tmp[len] = 0;
+               ptr += 2;
+
+               i = ipv6str_to_bytes(ptr, buf);
+               if(i == 0)
+               return i;
+
+               #if defined(ARGS_DEBUG)
+               int j;
+               dprintf("=========== bottom part i : %d \n",i);
+               for(j=0; j<i; j++)
+                       dprintf("%02x \t",buf[j]);
+               #endif
+
+               /* Copy the bottom part i.e bytes following "::" */
+               memcpy(ip+(16-i), buf, i);
+
+               k = ipv6str_to_bytes(tmp, buf);
+               if(k == 0)
+                       return k;
+
+               #if defined(ARGS_DEBUG)
+               dprintf("=========== top part k : %d \n",k);
+               for(j=0; j<k; j++)
+                       printf("%02x \t",buf[j]);
+               #endif
+
+               /* Copy the top part i.e bytes before "::"  */
+               memcpy(ip, buf, k);
+               #if defined(ARGS_DEBUG)
+               dprintf("\n");
+               for(j=0; j<16; j++)
+                       dprintf("%02x \t",ip[j]);
+               #endif
+
+       } else {
+               i = ipv6str_to_bytes(str, (char *)ip);
+       }
+       return i;
+}
+
+void ipv6_to_str(const uint8_t *ip, char *str)
+{
+       int i, len;
+       uint8_t byte_even, byte_odd;
+       char *consec_zero, *strptr;
+
+       *str = 0;
+       for (i = 0; i < 16; i+=2) {
+               byte_even = ip[i];
+               byte_odd = ip[i+1];
+               if (byte_even)
+                       sprintf(str, "%s%x%02x", str, byte_even, byte_odd);
+               else if (byte_odd)
+                       sprintf(str, "%s%x", str, byte_odd);
+               else
+                       strcat(str, "0");
+               if (i != 14)
+                       strcat(str, ":");
+       }
+       strptr = str;
+       do {
+               consec_zero = strstr(strptr, "0:0:");
+               if (consec_zero) {
+                       len = consec_zero - strptr;
+                       if (!len)
+                               break;
+                       else if (strptr[len-1] == ':')
+                               break;
+                       else
+                               strptr = consec_zero + 2;
+               }
+       } while (consec_zero);
+       if (consec_zero) {
+               len = consec_zero - str;
+               str[len] = 0;
+               if (len)
+                       strcat(str, ":");
+               else
+                       strcat(str, "::");
+               strptr = consec_zero + 4;
+               while (*strptr) {
+                       if (!strncmp(strptr, "0:", 2))
+                               strptr += 2;
+                       else
+                               break;
+               }
+               strcat(str, strptr);
+               if (!strcmp(str, "::0"))
+                       strcpy(str, "::");
+       }
+}
diff --git a/pc-bios/s390-ccw/libnet/ipv6.h b/pc-bios/s390-ccw/libnet/ipv6.h
new file mode 100644
index 0000000..6f783b3
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ipv6.h
@@ -0,0 +1,188 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 _IPV6_H_
+#define _IPV6_H_
+
+#include <stdint.h>
+#include "ethernet.h"
+
+#define __IPV6_DEBUG__
+
+#ifdef __IPV6_DEBUG__
+#define IPV6_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } 
while (0)
+#else
+#define IPV6_DEBUG_PRINT(format, ...)
+#endif
+
+#define IPV6_ADDR_LENGTH        16 /* Size of IPv6 adress in bytes */
+#define IPV6_LL_PREFIX          0xFE80000000000000ULL
+#define IPV6_LL_PREFIX_MASK     0xFFC0000000000000ULL
+#define IPV6_SOLIC_NODE_PREFIX   0xFF02000000000000ULL
+#define IPV6_SOLIC_NODE_IFACE_ID 0x00000001FF000000ULL
+
+/**
+ *  An IPv6 Address
+ */
+typedef union {
+       uint8_t addr[IPV6_ADDR_LENGTH];
+       struct {
+               uint64_t prefix;
+               uint64_t interface_id;
+       } part;
+} ip6_addr_t;
+
+typedef struct {
+       uint8_t type;
+       uint8_t pad[7];
+       union {
+               ip6_addr_t  v6;
+               char        v4[4];
+       } addr;
+} netaddr_t;
+
+/** \struct prefix_info
+ *
+ * List of Prefixes we have adresses from
+ * Used for internal purposes, information derived from prefix option
+ * in Router Advertisements
+ * See RFC 4861 section 4.6.2
+ */
+struct prefix_info {
+       uint64_t prefix;
+       uint8_t  on_link:1,         /* When set prefix can be used for on-link
+                                     * determination */
+                autoconf:1,        /* Prefix can be used for stateless address
+                                     * configuration */
+                reserved1:6;
+       uint32_t valid_lifetime;     /* Time until prefix expires */
+       uint32_t preferred_lifetime; /* Time until prefix becomes deprecated */
+       uint32_t start_time;         /* Time when received */
+       uint32_t reserved2;
+       struct   prefix_info *next;
+};
+
+
+/* List of IPv6 addresses */
+struct ip6addr_list_entry {
+       ip6_addr_t addr;
+       struct prefix_info prfx_info;
+       struct ip6addr_list_entry *next;
+};
+
+/** \struct ip6hdr
+ *  A header for IPv6 packets.
+ *  For more information see RFC 2460
+ */
+struct ip6hdr {
+       uint32_t ver_tc_fl;     /**< Version, Traffic class, Flow label */
+       uint16_t pl;            /**< Payload length                     */
+       uint8_t  nh;            /**< Next header                        */
+       uint8_t  hl;            /**< Hop limit                          */
+       ip6_addr_t src;         /**< IPv6 source address                */
+       ip6_addr_t dst;         /**< IPv6 destination address           */
+} __attribute((packed));
+
+/** \struct packeth
+ * Struct with pointers to headers within a packet
+ */
+struct packeth {
+       struct ethhdr  *ethh;
+       struct ip6hdr  *ip6h;
+       struct icmp6hdr  *icmp6h;
+       struct udphdr  *udph;
+       /* ... */
+};
+
+/** \struct parseip6_state
+ * Stores information about state of IPv6 address parser
+ */
+struct parseip6_state {
+       char *lookahead;
+       char *ptr;
+       const char *addr;
+       int state;
+       int s1ctr;
+       int s2ctr;
+       int blocknr;
+       int zeroblocks;
+       int i;
+       int done;
+       int errorcode;
+};
+
+/** \struct ip6_config
+ * Stores flags wheter we use Stateless- or Stateful Autoconfiguration or 
DHCPv6
+ */
+struct ip6_config {
+       uint8_t managed_mode:1,
+               other_config:1,
+               reserved:6;
+};
+
+/******************** VARIABLES **********************************************/
+/* Function pointer send_ip. Points either to send_ipv4() or send_ipv6() */
+extern int   (*send_ip) (int fd, void *, int);
+
+extern struct ip6_config ip6_state;
+
+/******************** FUNCTIONS *********************************************/
+/* Handles IPv6-packets that are detected by receive_ether. */
+int8_t handle_ipv6(int fd, uint8_t * ip6_packet, uint32_t packetsize);
+
+/* Fill IPv6 header */
+void fill_ip6hdr(uint8_t * packet, uint16_t packetsize,
+                uint8_t ip_proto, ip6_addr_t *ip6_src, ip6_addr_t *ip6_dst);
+
+/* Set own IPv6 address */
+void set_ipv6_address(int fd, ip6_addr_t *own_ip6);
+
+/* Get own IPv6 address */
+ip6_addr_t *get_ipv6_address(void);
+
+/* Create link-local address from a given Mac Address */
+void ip6_create_ll_address (const uint8_t *own_mac, ip6_addr_t *ll_addr);
+
+/* For a given MAC calculates EUI64-Identifier.*/
+uint64_t mac2eui64 (const uint8_t *mac);
+
+/* Create empty element for prefix list and return a pointer to it */
+struct prefix_info * ip6_create_prefix_info(void);
+
+/* Create a new IPv6 address with a given network prefix
+ *     and add it to our IPv6 address list */
+void * ip6_prefix2addr (ip6_addr_t prefix);
+
+/* Compare IPv6 adresses */
+int8_t ip6_cmp( ip6_addr_t *ip_1, ip6_addr_t *ip_2 );
+
+/* Check if it is a link-local address */
+static inline int ip6_is_linklocal(ip6_addr_t *ip)
+{
+       return (ip->part.prefix & IPV6_LL_PREFIX_MASK) == IPV6_LL_PREFIX;
+}
+
+/* Check if prefix is already in our list */
+int8_t unknown_prefix (ip6_addr_t *ip);
+
+/* Send IPv6 packet */
+int send_ipv6 (int fd, void* buffer, int len);
+
+/* Add IPv6 address to list */
+int8_t ip6addr_add (struct ip6addr_list_entry *new_address);
+
+/* Parse an IPv6 address */
+int parseip6(const char *addr, uint8_t *parsedaddr);
+int str_to_ipv6(const char *str, uint8_t *ip);
+void ipv6_to_str(const uint8_t *ip, char *str);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/ndp.c b/pc-bios/s390-ccw/libnet/ndp.c
new file mode 100644
index 0000000..f1f51c7
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ndp.c
@@ -0,0 +1,184 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "ipv6.h"
+#include "icmpv6.h"
+#include "ndp.h"
+
+/* Neighbor cache */
+static struct neighbor *first_neighbor;
+static struct neighbor *last_neighbor;
+
+/* Router list */
+static struct router *first_router;
+static struct router *last_router;
+
+/*
+ * NET: add new router to list
+ * @param  struct router nghb  - new router
+ * @return true or false
+ */
+int8_t
+router_add (struct router *nghb )
+{
+       if (nghb == NULL)
+               return -1;
+
+       if (first_router == NULL) {
+               first_router= nghb;
+               last_router = first_router;
+               last_router->next = NULL;
+       } else {
+               last_router->next = nghb;
+               last_router = nghb;
+               last_router->next = NULL;
+       }
+       return 1; /* no error */
+}
+
+/*
+ * NET: create a new router
+ * @param  uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct)
+ * @param  struct packeth  - pointers to headers in packet
+ * @return pointer to new router
+ */
+void *
+router_create (uint8_t *mac, ip6_addr_t *ip)
+{
+       struct router *new_router;
+
+       new_router = malloc (sizeof(struct router));
+       if( !new_router) {
+               return 0;
+       }
+       memset (new_router, 0, sizeof(struct router));
+
+       /* fill neighbor struct */
+       memcpy (new_router->mac, mac, 6);
+       memcpy (&(new_router->ip.addr[0]), &(ip->addr[0]), IPV6_ADDR_LENGTH);
+
+       return new_router;
+}
+
+struct router *
+find_router( ip6_addr_t *ip )
+{
+       struct router *n = NULL;
+
+       for (n = first_router; n != NULL ; n=n->next)
+               if (ip6_cmp (&(n->ip), ip))
+                       return n; /* router is already in list*/
+
+       return NULL; /* router is unknown */
+}
+
+/**
+ * Find a router for a given host address
+ * @param  ip - IPv6 address with the prefered prefix
+ * @return pointer to router, or NULL if none is available
+ */
+struct router *ipv6_get_default_router(ip6_addr_t *ip)
+{
+       struct router *n = NULL;
+
+       for (n = first_router; n != NULL; n = n->next) {
+               if (n->ip.part.prefix == ip->part.prefix)
+                       return n;
+       }
+
+       return first_router;
+}
+
+/*
+ * NET: add new neighbor to list
+ * @param  struct neighbor nghb  - new neighbor
+ * @return true or false
+ */
+int8_t
+neighbor_add (struct neighbor *nghb)
+{
+       if (nghb == NULL)
+               return -1;
+
+       if (first_neighbor == NULL) {
+               first_neighbor = nghb;
+               last_neighbor = first_neighbor;
+               last_neighbor->next = NULL;
+       } else {
+               last_neighbor->next = nghb;
+               last_neighbor = nghb;
+               last_neighbor->next = NULL;
+       }
+
+       return 1; /* no error */
+}
+
+/*
+ * NET: create a new neighbor
+ * @param  uint8_t *packet - received packet (Ethernet/IPv6/ICMPv6/ND_NghSlct)
+ * @param  struct packeth  - pointers to headers in packet
+ * @return pointer         - pointer to new neighbor
+ *         NULL            - malloc failed
+ */
+void *
+neighbor_create (uint8_t *packet, struct packeth *headers)
+{
+       struct neighbor *new_neighbor;
+
+       new_neighbor = malloc (sizeof(struct neighbor));
+       if( !new_neighbor )
+               return NULL;
+       memset(new_neighbor, 0, sizeof(struct neighbor));
+
+       /* fill neighbor struct */
+       memcpy (&(new_neighbor->mac),
+               &(headers->ethh->src_mac[0]), 6);
+       memcpy (&(new_neighbor->ip.addr), &(headers->ip6h->src), 
IPV6_ADDR_LENGTH);
+       new_neighbor->status = NB_INCOMPLETE;
+
+       return new_neighbor;
+}
+
+/**
+ * NET: Find neighbor with given IPv6 address in Neighbor Cache
+ *
+ * @param  ip - Pointer to IPv6 address
+ * @return pointer - pointer to client IPv6 address (e.g. ::1)
+ *         NULL    - Neighbor not found
+ */
+struct neighbor *
+find_neighbor (ip6_addr_t *ip)
+{
+       struct neighbor *n = NULL;
+
+       for (n = first_neighbor; n != NULL ; n=n->next) {
+               if (ip6_cmp (&(n->ip), ip)) {
+                       return n; /* neighbor is already in cache */
+               }
+       }
+
+       return NULL; /* neighbor is unknown */
+}
+
+void ndp_init(void)
+{
+       /* Router list */
+       first_router = NULL;
+       last_router = first_router;
+
+       /* Init Neighbour cache */
+       first_neighbor = NULL;
+       last_neighbor  = first_neighbor;
+}
diff --git a/pc-bios/s390-ccw/libnet/ndp.h b/pc-bios/s390-ccw/libnet/ndp.h
new file mode 100644
index 0000000..cd18158
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/ndp.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ * Copyright (c) 2013 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 _NDP_H_
+#define _NDP_H_
+
+#include "ipv6.h"
+
+#define __NDP_DEBUG__
+
+#ifdef __NDP_DEBUG__
+#define NDP_DEBUG_PRINT(format, ...) do { printf(format, ## __VA_ARGS__); } 
while (0)
+#else
+#define NDP_DEBUG_PRINT(format, ...)
+#endif
+
+#define ND_OPTION_SOURCE_LL_ADDR  1
+#define ND_OPTION_TARGET_LL_ADDR  2
+#define ND_OPTION_PREFIX_INFO     3
+#define ND_OPTION_REDIRECT_HDR    4
+#define ND_OPTION_MTU             5
+
+/* Default Router List */
+struct router {
+       uint8_t  mac[6];
+       ip6_addr_t ip;
+       uint32_t lifetime;
+       uint32_t reachable_time;
+       uint32_t retrans_timer;
+       struct router *next;
+};
+
+/* Neighbor cache */
+struct neighbor {
+       uint8_t mac[6];
+       ip6_addr_t ip;
+       uint8_t is_router;
+       uint8_t status;
+       uint8_t times_asked;
+       /* ... */
+       struct neighbor *next;
+       uint8_t eth_frame[ETH_MTU_SIZE];
+       uint32_t eth_len;
+
+#define NB_INCOMPLETE 1
+#define NB_REACHABLE  2
+#define NB_STALE      3
+#define NB_DELAY      4
+#define NB_PROBE      5
+};
+
+/******************** FUNCTIONS *********************************************/
+void ndp_init(void);
+int8_t neighbor_add (struct neighbor *);
+void * neighbor_create (uint8_t *packet, struct packeth *headers);
+struct neighbor * find_neighbor (ip6_addr_t *);
+
+int8_t router_add(struct router*);
+void * router_create(uint8_t *mac, ip6_addr_t *ip);
+struct router * find_router(ip6_addr_t *);
+struct router *ipv6_get_default_router(ip6_addr_t *ip);
+
+#endif //_NDP_H_
diff --git a/pc-bios/s390-ccw/libnet/netapps.h 
b/pc-bios/s390-ccw/libnet/netapps.h
new file mode 100644
index 0000000..91c1ebd
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/netapps.h
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * 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_
+
+#define F_IPV4 4
+#define F_IPV6 6
+
+struct filename_ip;
+
+extern int netload(char *buffer, int len, char *ret_buffer, int huge_load,
+                  int block_size, char *args_fs, int alen);
+extern int netsave(int argc, char *argv[]);
+extern int ping(char *args_fs, int alen);
+extern int dhcp(char *ret_buffer, struct filename_ip *fn_ip,
+               unsigned int retries, int flags);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/netload.c 
b/pc-bios/s390-ccw/libnet/netload.c
new file mode 100644
index 0000000..cd3720a
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/netload.c
@@ -0,0 +1,868 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <unistd.h>
+#include <tftp.h>
+#include <ethernet.h>
+#include <dhcp.h>
+#include <dhcpv6.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <dns.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <libbootmsg/libbootmsg.h>
+#include <helpers.h>
+#include "args.h"
+#include "netapps.h"
+
+#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, struct filename_ip *fn_ip, unsigned int retries,
+        int flags)
+{
+       int i = (int) retries+1;
+       int rc = -1;
+
+       printf("  Requesting information via DHCP%s:     ",
+              flags == F_IPV4 ? "v4" : flags == F_IPV6 ? "v6" : "");
+
+       if (flags != F_IPV6)
+               dhcpv4_generate_transaction_id();
+       if (flags != F_IPV4)
+               dhcpv6_generate_transaction_id();
+
+       do {
+               printf("\b\b\b%03d", i-1);
+               if (getchar() == 27) {
+                       printf("\nAborted\n");
+                       return -1;
+               }
+               if (!--i) {
+                       printf("\nGiving up after %d DHCP requests\n", retries);
+                       return -1;
+               }
+               if (!flags || (flags == F_IPV4)) {
+                       ip_version = 4;
+                       rc = dhcpv4(ret_buffer, fn_ip);
+               }
+               if ((!flags && (rc == -1)) || (flags == F_IPV6)) {
+                       ip_version = 6;
+                       set_ipv6_address(fn_ip->fd, 0);
+                       rc = dhcpv6(ret_buffer, fn_ip);
+                       if (rc == 0) {
+                               memcpy(&fn_ip->own_ip6, get_ipv6_address(), 16);
+                               break;
+                       }
+
+               }
+               if (rc != -1) /* either success or non-dhcp failure */
+                       break;
+       } while (1);
+       printf("\b\b\b\bdone\n");
+
+       return rc;
+}
+
+/**
+ * Seed the random number generator with our mac and current timestamp
+ */
+static void seed_rng(uint8_t mac[])
+{
+       unsigned int seed;
+
+       asm volatile("mftbl %0" : "=r"(seed));
+       seed ^= (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5];
+       srand(seed);
+}
+
+int netload(char *buffer, int len, char *ret_buffer, int huge_load,
+           int block_size, char *args_fs, int alen)
+{
+       char buf[256];
+       int rc;
+       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 };
+       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 (alen > 0) {
+               char args[256];
+               if (alen > sizeof(args) - 1) {
+                       puts("ERROR: Parameter string is too long.");
+                       return -7;
+               }
+               /* Convert forth string into NUL-terminated C-string */
+               strncpy(args, args_fs, alen);
+               args[alen] = 0;
+               parse_args(args, &obp_tftp_args);
+               if(obp_tftp_args.bootp_retries - rc < DEFAULT_BOOT_RETRIES)
+                       obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
+               else
+                       obp_tftp_args.bootp_retries -= rc;
+       }
+       else {
+               memset(&obp_tftp_args, 0, sizeof(obp_tftp_args_t));
+               obp_tftp_args.ip_init = IP_INIT_DEFAULT;
+               obp_tftp_args.bootp_retries = DEFAULT_BOOT_RETRIES;
+               obp_tftp_args.tftp_retries = DEFAULT_TFTP_RETRIES;
+       }
+       memcpy(&fn_ip.own_ip, obp_tftp_args.ciaddr, 4);
+
+       //  reset of error code
+       rc = 0;
+
+       /* if we still have got all necessary parameters, then we don't
+          need to perform an BOOTP/DHCP-Request */
+       if (ip_version == 4) {
+               if (memcmp(obp_tftp_args.ciaddr, null_ip, 4) != 0
+                   && memcmp(obp_tftp_args.siaddr, null_ip, 4) != 0
+                   && obp_tftp_args.filename[0] != 0) {
+
+                       memcpy(&fn_ip.server_ip, &obp_tftp_args.siaddr, 4);
+                       obp_tftp_args.ip_init = IP_INIT_NONE;
+               }
+       }
+       else if (ip_version == 6) {
+               if (memcmp(&obp_tftp_args.si6addr, null_ip6, 16) != 0
+                   && obp_tftp_args.filename[0] != 0) {
+                       memcpy(&fn_ip.server_ip6.addr[0],
+                              &obp_tftp_args.si6addr.addr, 16);
+                       obp_tftp_args.ip_init = IP_INIT_IPV6_MANUAL;
+               }
+               else {
+                       obp_tftp_args.ip_init = IP_INIT_DHCPV6_STATELESS;
+               }
+       }
+
+       // construction of fn_ip from parameter
+       switch(obp_tftp_args.ip_init) {
+       case IP_INIT_BOOTP:
+               // if giaddr in not specified, then we have to identify
+               // the BOOTP server via broadcasts
+               if(memcmp(obp_tftp_args.giaddr, null_ip, 4) == 0) {
+                       // don't do this, when using DHCP !!!
+                       fn_ip.server_ip = 0xFFFFFFFF;
+               }
+               // if giaddr is specified, then we have to use this
+               // IP address as proxy to identify the BOOTP server
+               else {
+                       memcpy(&fn_ip.server_ip, obp_tftp_args.giaddr, 4);
+               }
+               rc = bootp(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);
+               close(fn_ip.fd);
+               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);
+               close(fn_ip.fd);
+               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);
+               close(fn_ip.fd);
+               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);
+
+       close(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/pc-bios/s390-ccw/libnet/tcp.c b/pc-bios/s390-ccw/libnet/tcp.c
new file mode 100644
index 0000000..faa0b83
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/tcp.c
@@ -0,0 +1,46 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+/************************ DEFINITIONS & DECLARATIONS *********************/
+
+#include <tcp.h>
+#include <sys/socket.h>
+
+/****************************** LOCAL VARIABLES **************************/
+
+/****************************** IMPLEMENTATION ***************************/
+
+/**
+ * TCP: Handles TCP-packets according to Receive-handle diagram.
+ *
+ * @param  tcp_packet TCP-packet to be handled
+ * @param  packetsize Length of the packet
+ * @return            ZERO - packet handled successfully;
+ *                    NON ZERO - packet was not handled (e.g. bad format)
+ */
+int8_t handle_tcp(uint8_t * tcp_packet, int32_t packetsize)
+{
+       return -1;
+}
+
+/**
+ * NET: This function handles situation when "Destination unreachable"
+ *      ICMP-error occurs during sending TCP-packet.
+ *
+ * @param  err_code   Error Code (e.g. "Host unreachable")
+ * @param  packet     original TCP-packet
+ * @param  packetsize length of the packet
+ * @see               handle_icmp
+ */
+void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t 
err_code)
+{
+}
diff --git a/pc-bios/s390-ccw/libnet/tcp.h b/pc-bios/s390-ccw/libnet/tcp.h
new file mode 100644
index 0000000..375afd7
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/tcp.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * 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 _TCP_H
+#define _TCP_H
+
+#include <stdint.h>
+
+#define IPTYPE_TCP          6
+
+/* Handles TCP-packets that are detected by any network layer. */
+extern int8_t handle_tcp(uint8_t * udp_packet, int32_t packetsize);
+
+/* Handles TCP related ICMP-Dest.Unreachable packets that are detected by
+ * the network layers. */
+extern void handle_tcp_dun(uint8_t * tcp_packet, uint32_t packetsize, uint8_t 
err_code);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/tftp.c b/pc-bios/s390-ccw/libnet/tftp.c
new file mode 100644
index 0000000..d0c2f13
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/tftp.c
@@ -0,0 +1,594 @@
+/******************************************************************************
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/socket.h>
+
+#include <ethernet.h>
+#include <ipv4.h>
+#include <ipv6.h>
+#include <udp.h>
+
+//#define __DEBUG__
+
+#define MAX_BLOCKSIZE 1428
+#define BUFFER_LEN 256
+
+#define ENOTFOUND 1
+#define EACCESS   2
+#define EBADOP    4
+#define EBADID    5
+#define ENOUSER   7
+//#define EUNDEF 0
+//#define ENOSPACE 3
+//#define EEXISTS 6
+
+#define RRQ   1
+#define WRQ   2
+#define DATA  3
+#define ACK   4
+#define ERROR 5
+#define OACK  6
+
+/* Local variables */
+static unsigned char packet[BUFFER_LEN];
+static unsigned char  *buffer = NULL;
+static unsigned short block;
+static unsigned short blocksize;
+static char blocksize_str[6];    /* Blocksize string for read request */
+static int received_len;
+static unsigned int retries;
+static int huge_load;
+static int len;
+static int tftp_finished;
+static int lost_packets;
+static int tftp_errno;
+static int ip_version;
+static short port_number;
+static tftp_err_t *tftp_err;
+static filename_ip_t  *fn_ip;
+static int progress_first;
+static int progress_last_bytes;
+
+/**
+ * dump_package - Prints a package.
+ *
+ * @package: package which is to print
+ * @len:     length of the package
+ */
+#ifdef __DEBUG__
+
+static void dump_package(unsigned char *buffer, unsigned int len)
+{
+       int i;
+
+       for (i = 1; i <= len; i++) {
+               printf("%02x%02x ", buffer[i - 1], buffer[i]);
+               i++;
+               if ((i % 16) == 0)
+                       printf("\n");
+       }
+       printf("\n");
+}
+#endif
+
+/**
+ * send_rrq - Sends a read request package.
+ *
+ * @fd:          Socket Descriptor
+ */
+static void send_rrq(int fd)
+{
+       int ip_len = 0;
+       int ip6_payload_len    = 0;
+       unsigned short udp_len = 0;
+       unsigned char mode[] = "octet";
+       char *ptr            = NULL;
+       struct iphdr *ip     = NULL;
+       struct ip6hdr *ip6   = NULL;
+       struct udphdr *udph  = NULL;
+       struct tftphdr *tftp = NULL;
+
+       memset(packet, 0, BUFFER_LEN);
+
+       if (4 == ip_version) {
+               ip = (struct iphdr *) packet;
+               udph = (struct udphdr *) (ip + 1);
+               ip_len = sizeof(struct iphdr) + sizeof(struct udphdr)
+                       + strlen((char *) fn_ip->filename) + strlen((char *) 
mode) + 4
+                       + strlen("blksize") + strlen(blocksize_str) + 2;
+               fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0,
+                           fn_ip->server_ip);
+       }
+       else if (6 == ip_version) {
+               ip6 = (struct ip6hdr *) packet;
+               udph = (struct udphdr *) (ip6 + 1);
+               ip6_payload_len = sizeof(struct udphdr)
+                       + strlen((char *) fn_ip->filename) + strlen((char *) 
mode) + 4
+                       + strlen("blksize") + strlen(blocksize_str) + 2;
+               ip_len = sizeof(struct ip6hdr) + ip6_payload_len;
+               fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, 
get_ipv6_address(),
+                            &(fn_ip->server_ip6));
+
+       }
+       udp_len = htons(sizeof(struct udphdr)
+                             + strlen((char *) fn_ip->filename) + strlen((char 
*) mode) + 4
+                             + strlen("blksize") + strlen(blocksize_str) + 2);
+       fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(69));
+
+       tftp = (struct tftphdr *) (udph + 1);
+       tftp->th_opcode = htons(RRQ);
+
+       ptr = (char *) &tftp->th_data;
+       memcpy(ptr, fn_ip->filename, strlen((char *) fn_ip->filename) + 1);
+
+       ptr += strlen((char *) fn_ip->filename) + 1;
+       memcpy(ptr, mode, strlen((char *) mode) + 1);
+
+       ptr += strlen((char *) mode) + 1;
+       memcpy(ptr, "blksize", strlen("blksize") + 1);
+
+       ptr += strlen("blksize") + 1;
+       memcpy(ptr, blocksize_str, strlen(blocksize_str) + 1);
+
+       send_ip (fd, packet, ip_len);
+
+#ifdef __DEBUG__
+       printf("tftp RRQ with %d bytes transmitted.\n", ip_len);
+#endif
+       return;
+}
+
+/**
+ * send_ack - Sends a acknowlege package.
+ *
+ * @blckno: block number
+ * @dport:  UDP destination port
+ */
+static void send_ack(int fd, int blckno, unsigned short dport)
+{
+       int ip_len             = 0;
+       int ip6_payload_len    = 0;
+       unsigned short udp_len = 0;
+       struct iphdr *ip     = NULL;
+       struct ip6hdr *ip6   = NULL;
+       struct udphdr *udph  = NULL;
+       struct tftphdr *tftp = NULL;
+
+       memset(packet, 0, BUFFER_LEN);
+
+       if (4 == ip_version) {
+               ip = (struct iphdr *) packet;
+               udph = (struct udphdr *) (ip + 1);
+               ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 4;
+               fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0,
+                           fn_ip->server_ip);
+       }
+       else if (6 == ip_version) {
+               ip6 = (struct ip6hdr *) packet;
+               udph = (struct udphdr *) (ip6 + 1);
+               ip6_payload_len = sizeof(struct udphdr) + 4;
+               ip_len = sizeof(struct ip6hdr) + ip6_payload_len;
+               fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, 
get_ipv6_address(),
+                            &(fn_ip->server_ip6));
+       }
+       udp_len = htons(sizeof(struct udphdr) + 4);
+       fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport));
+
+       tftp = (struct tftphdr *) (udph + 1);
+       tftp->th_opcode = htons(ACK);
+       tftp->th_data = htons(blckno);
+
+       send_ip(fd, packet, ip_len);
+
+#ifdef __DEBUG__
+       printf("tftp ACK %d bytes transmitted.\n", ip_len);
+#endif
+
+       return;
+}
+
+/**
+ * send_error - Sends an error package.
+ *
+ * @fd:          Socket Descriptor
+ * @error_code:  Used sub code for error packet
+ * @dport:       UDP destination port
+ */
+static void send_error(int fd, int error_code, unsigned short dport)
+{
+       int ip_len             = 0;
+       int ip6_payload_len    = 0;
+       unsigned short udp_len = 0;
+       struct ip6hdr *ip6   = NULL;
+       struct iphdr *ip     = NULL;
+       struct udphdr *udph  = NULL;
+       struct tftphdr *tftp = NULL;
+
+       memset(packet, 0, BUFFER_LEN);
+
+       if (4 == ip_version) {
+               ip = (struct iphdr *) packet;
+               udph = (struct udphdr *) (ip + 1);
+               ip_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 5;
+               fill_iphdr ((uint8_t *) ip, ip_len, IPTYPE_UDP, 0,
+                           fn_ip->server_ip);
+       }
+       else if (6 == ip_version) {
+               ip6 = (struct ip6hdr *) packet;
+               udph = (struct udphdr *) (ip6 + 1);
+               ip6_payload_len = sizeof(struct udphdr) + 5;
+               ip_len = sizeof(struct ip6hdr) + ip6_payload_len;
+               fill_ip6hdr ((uint8_t *) ip6, ip6_payload_len, IPTYPE_UDP, 
get_ipv6_address(),
+                           &(fn_ip->server_ip6));
+       }
+       udp_len = htons(sizeof(struct udphdr) + 5);
+       fill_udphdr ((uint8_t *) udph, udp_len, htons(2001), htons(dport));
+
+       tftp = (struct tftphdr *) (udph + 1);
+       tftp->th_opcode = htons(ERROR);
+       tftp->th_data = htons(error_code);
+       ((char *) &tftp->th_data)[2] = 0;
+
+       send_ip(fd, packet, ip_len);
+
+#ifdef __DEBUG__
+       printf("tftp ERROR %d bytes transmitted.\n", ip_len);
+#endif
+
+       return;
+}
+
+static void print_progress(int urgent, int received_bytes)
+{
+       static unsigned int i = 1;
+       char buffer[100];
+       char *ptr;
+
+       // 1MB steps or 0x400 times or urgent
+       if(((received_bytes - progress_last_bytes) >> 20) > 0
+       || (i & 0x3FF) == 0 || urgent) {
+               if (!progress_first) {
+                       sprintf(buffer, "%d KBytes", (progress_last_bytes >> 
10));
+                       for(ptr = buffer; *ptr != 0; ++ptr)
+                               *ptr = '\b';
+                       printf(buffer);
+               }
+               printf("%d KBytes", (received_bytes >> 10));
+               i = 1;
+               progress_first = 0;
+               progress_last_bytes = received_bytes;
+       }
+       ++i;
+}
+
+/**
+ * get_blksize tries to extract the blksize from the OACK package
+ * the TFTP returned. From RFC 1782
+ * The OACK packet has the following format:
+ *
+ *   +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ *   |  opc  |  opt1  | 0 | value1 | 0 |  optN  | 0 | valueN | 0 |
+ *   +-------+---~~---+---+---~~---+---+---~~---+---+---~~---+---+
+ *
+ * @param buffer  the network packet
+ * @param len  the length of the network packet
+ * @return  the blocksize the server supports or 0 for error
+ */
+static int get_blksize(unsigned char *buffer, unsigned int len)
+{
+       unsigned char *orig = buffer;
+       /* skip all headers until tftp has been reached */
+       buffer += sizeof(struct udphdr);
+       /* skip opc */
+       buffer += 2;
+       while (buffer < orig + len) {
+               if (!memcmp(buffer, "blksize", strlen("blksize") + 1))
+                       return (unsigned short) strtoul((char *) (buffer +
+                                                       strlen("blksize") + 1),
+                                                       (char **) NULL, 10);
+               else {
+                       /* skip the option name */
+                       buffer = (unsigned char *) strchr((char *) buffer, 0);
+                       if (!buffer)
+                               return 0;
+                       buffer++;
+                       /* skip the option value */
+                       buffer = (unsigned char *) strchr((char *) buffer, 0);
+                       if (!buffer)
+                               return 0;
+                       buffer++;
+               }
+       }
+       return 0;
+}
+
+/**
+ * Handle incoming tftp packets after read request was sent
+ *
+ * this function also prints out some status characters
+ * \|-/ for each packet received
+ * A for an arp packet
+ * I for an ICMP packet
+ * #+* for different unexpected TFTP packets (not very good)
+ *
+ * @param fd     socket descriptor
+ * @param packet points to the UDP header of the packet
+ * @param len    the length of the network packet
+ * @return       ZERO if packet was handled successfully
+ *               ERRORCODE if error occurred
+ */
+int32_t handle_tftp(int fd, uint8_t *pkt, int32_t packetsize)
+{
+       struct udphdr *udph;
+       struct tftphdr *tftp;
+
+       /* buffer is only set if we are handling TFTP */
+       if (buffer == NULL )
+               return 0;
+
+#ifndef __DEBUG__
+       print_progress(0, received_len);
+#endif
+       udph = (struct udphdr *) pkt;
+       tftp = (struct tftphdr *) ((void *) udph + sizeof(struct udphdr));
+       set_timer(TICKS_SEC);
+
+#ifdef __DEBUG__
+       dump_package(pkt, packetsize);
+#endif
+
+       port_number = udph->uh_sport;
+       if (tftp->th_opcode == htons(OACK)) {
+               /* an OACK means that the server answers our blocksize request 
*/
+               blocksize = get_blksize(pkt, packetsize);
+               if (!blocksize || blocksize > MAX_BLOCKSIZE) {
+                       send_error(fd, 8, port_number);
+                       tftp_errno = -8;
+                       goto error;
+               }
+               send_ack(fd, 0, port_number);
+       } else if (tftp->th_opcode == htons(ACK)) {
+               /* an ACK means that the server did not answers
+                * our blocksize request, therefore we will set the blocksize
+                * to the default value of 512 */
+               blocksize = 512;
+               send_ack(fd, 0, port_number);
+       } else if ((unsigned char) tftp->th_opcode == ERROR) {
+#ifdef __DEBUG__
+               printf("tftp->th_opcode : %x\n", tftp->th_opcode);
+               printf("tftp->th_data   : %x\n", tftp->th_data);
+#endif
+               switch ( (uint8_t) tftp->th_data) {
+               case ENOTFOUND:
+                       tftp_errno = -3;        // ERROR: file not found
+                       break;
+               case EACCESS:
+                       tftp_errno = -4;        // ERROR: access violation
+                       break;
+               case EBADOP:
+                       tftp_errno = -5;        // ERROR: illegal TFTP operation
+                       break;
+               case EBADID:
+                       tftp_errno = -6;        // ERROR: unknown transfer ID
+                       break;
+               case ENOUSER:
+                       tftp_errno = -7;        // ERROR: no such user
+                       break;
+               default:
+                       tftp_errno = -1;        // ERROR: unknown error
+               }
+               goto error;
+       } else if (tftp->th_opcode == DATA) {
+               /* DATA PACKAGE */
+               if (block + 1 == tftp->th_data) {
+                       ++block;
+               }
+               else if( block == 0xffff && huge_load != 0
+                    &&  (tftp->th_data == 0 || tftp->th_data == 1) ) {
+                       block = tftp->th_data;
+               }
+               else if (tftp->th_data == block) {
+#ifdef __DEBUG__
+                       printf
+                           ("\nTFTP: Received block %x, expected block was 
%x\n",
+                            tftp->th_data, block + 1);
+                       printf("\b+ ");
+#endif
+                       send_ack(fd, tftp->th_data, port_number);
+                       lost_packets++;
+                       tftp_err->bad_tftp_packets++;
+                       return 0;
+               } else if (tftp->th_data < block) {
+#ifdef __DEBUG__
+                       printf
+                           ("\nTFTP: Received block %x, expected block was 
%x\n",
+                            tftp->th_data, block + 1);
+                       printf("\b* ");
+#endif
+                       /* This means that an old data packet appears (again);
+                        * this happens sometimes if we don't answer fast enough
+                        * and a timeout is generated on the server side;
+                        * as we already have this packet we just ignore it */
+                       tftp_err->bad_tftp_packets++;
+                       return 0;
+               } else {
+                       tftp_err->blocks_missed = block + 1;
+                       tftp_err->blocks_received = tftp->th_data;
+                       tftp_errno = -42;
+                       goto error;
+               }
+               tftp_err->bad_tftp_packets = 0;
+               /* check if our buffer is large enough */
+               if (received_len + udph->uh_ulen - 12 > len) {
+                       tftp_errno = -2;
+                       goto error;
+               }
+               memcpy(buffer + received_len, &tftp->th_data + 1,
+                      udph->uh_ulen - 12);
+               send_ack(fd, tftp->th_data, port_number);
+               received_len += udph->uh_ulen - 12;
+               /* Last packet reached if the payload of the UDP packet
+                * is smaller than blocksize + 12
+                * 12 = UDP header (8) + 4 bytes TFTP payload */
+               if (udph->uh_ulen < blocksize + 12) {
+                       tftp_finished = 1;
+                       return 0;
+               }
+               /* 0xffff is the highest block number possible
+                * see the TFTP RFCs */
+
+               if (block >= 0xffff && huge_load == 0) {
+                       tftp_errno = -9;
+                       goto error;
+               }
+       } else {
+#ifdef __DEBUG__
+               printf("Unknown packet %x\n", tftp->th_opcode);
+               printf("\b# ");
+#endif
+               tftp_err->bad_tftp_packets++;
+               return 0;
+       }
+
+       return 0;
+
+error:
+#ifdef __DEBUG__
+       printf("\nTFTP errno: %d\n", tftp_errno);
+#endif
+       tftp_finished = 1;
+       return tftp_errno;
+}
+
+/**
+ * TFTP: This function handles situation when "Destination unreachable"
+ *       ICMP-error occurs during sending TFTP-packet.
+ *
+ * @param  err_code   Error Code (e.g. "Host unreachable")
+ */
+void handle_tftp_dun(uint8_t err_code)
+{
+       tftp_errno = - err_code - 10;
+       tftp_finished = 1;
+}
+
+/**
+ * TFTP: Interface function to load files via TFTP.
+ *
+ * @param  _fn_ip        contains the following configuration information:
+ *                       client IP, TFTP-server IP, filename to be loaded
+ * @param  _buffer       destination buffer for the file
+ * @param  _len          size of destination buffer
+ * @param  _retries      max number of retries
+ * @param  _tftp_err     contains info about TFTP-errors (e.g. lost packets)
+ * @param  _mode         NON ZERO - multicast, ZERO - unicast
+ * @param  _blocksize    blocksize for DATA-packets
+ * @return               ZERO - error condition occurs
+ *                       NON ZERO - size of received file
+ */
+int tftp(filename_ip_t * _fn_ip, unsigned char *_buffer, int _len,
+        unsigned int _retries, tftp_err_t * _tftp_err,
+        int32_t _mode, int32_t _blocksize, int _ip_version)
+{
+       retries     = _retries;
+       fn_ip       = _fn_ip;
+       len         = _len;
+       huge_load   = _mode;
+       ip_version  = _ip_version;
+       tftp_errno  = 0;
+       tftp_err    = _tftp_err;
+       tftp_err->bad_tftp_packets = 0;
+       tftp_err->no_packets = 0;
+
+       block = 0;
+       received_len = 0;
+       tftp_finished = 0;
+       lost_packets = 0;
+       port_number = -1;
+       progress_first = -1;
+       progress_last_bytes = 0;
+
+       /* Default blocksize must be 512 for TFTP servers
+        * which do not support the RRQ blocksize option */
+       blocksize = 512;
+
+       /* Preferred blocksize - used as option for the read request */
+       if (_blocksize < 8)
+               _blocksize = 8;
+       else if (_blocksize > MAX_BLOCKSIZE)
+               _blocksize = MAX_BLOCKSIZE;
+       sprintf(blocksize_str, "%d", _blocksize);
+
+       printf("  Receiving data:  ");
+       print_progress(-1, 0);
+
+       // Setting buffer to a non-zero address enabled handling of received 
TFTP packets.
+       buffer = _buffer;
+
+       set_timer(TICKS_SEC);
+       send_rrq(fn_ip->fd);
+
+       while (! tftp_finished) {
+               /* if timeout (no packet received) */
+               if(get_timer() <= 0) {
+                       /* the server doesn't seem to retry let's help out a 
bit */
+                       if (tftp_err->no_packets > 4 && port_number != -1
+                           && block > 1) {
+                               send_ack(fn_ip->fd, block, port_number);
+                       }
+                       else if (port_number == -1 && block == 0
+                                && (tftp_err->no_packets&3) == 3) {
+                               printf("\nRepeating TFTP read request...\n");
+                               send_rrq(fn_ip->fd);
+                       }
+                       tftp_err->no_packets++;
+                       set_timer(TICKS_SEC);
+               }
+
+               /* handle received packets */
+               receive_ether(fn_ip->fd);
+
+               /* bad_tftp_packets are counted whenever we receive a TFTP 
packet
+                       * which was not expected; if this gets larger than 
'retries'
+                       * we just exit */
+               if (tftp_err->bad_tftp_packets > retries) {
+                       tftp_errno = -40;
+                       break;
+               }
+
+               /* no_packets counts the times we have returned from 
receive_ether()
+                       * without any packet received; if this gets larger than 
'retries'
+                       * we also just exit */
+               if (tftp_err->no_packets > retries) {
+                       tftp_errno = -41;
+                       break;
+               }
+       }
+
+       // Setting buffer to NULL disables handling of received TFTP packets.
+       buffer = NULL;
+
+       if (tftp_errno)
+               return tftp_errno;
+
+       print_progress(-1, received_len);
+       printf("\n");
+       if (lost_packets)
+               printf("Lost ACK packets: %d\n", lost_packets);
+
+       return received_len;
+}
diff --git a/pc-bios/s390-ccw/libnet/tftp.h b/pc-bios/s390-ccw/libnet/tftp.h
new file mode 100644
index 0000000..303feaf
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/tftp.h
@@ -0,0 +1,52 @@
+/******************************************************************************
+ * 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 _TFTP_H_
+#define _TFTP_H_
+
+#include <stdint.h>
+#include "ipv6.h"
+
+struct tftphdr {
+       int16_t th_opcode;
+       uint16_t th_data;
+};
+
+struct filename_ip {
+       uint32_t own_ip;
+       ip6_addr_t own_ip6;
+       uint32_t server_ip;
+       ip6_addr_t server_ip6;
+       ip6_addr_t dns_ip6;
+       int8_t filename[256];
+       int    fd;
+} __attribute__ ((packed));
+typedef struct filename_ip filename_ip_t;
+
+typedef struct {
+       uint32_t bad_tftp_packets;
+       uint32_t no_packets;
+       uint32_t blocks_missed;
+       uint32_t blocks_received;
+} tftp_err_t;
+
+int tftp(filename_ip_t *, unsigned char  *, int, unsigned int,
+         tftp_err_t *, int32_t mode, int32_t blocksize, int ip_version);
+int tftp_netsave(filename_ip_t *, uint8_t * buffer, int len,
+                int use_ci, unsigned int retries, tftp_err_t * tftp_err);
+
+int32_t handle_tftp(int fd, uint8_t *, int32_t);
+void handle_tftp_dun(uint8_t err_code);
+int parse_tftp_args(char buffer[], char *server_ip, char filename[], int fd, 
int len);
+
+#endif
diff --git a/pc-bios/s390-ccw/libnet/time.h b/pc-bios/s390-ccw/libnet/time.h
new file mode 100644
index 0000000..fcd5cd5
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/time.h
@@ -0,0 +1,6 @@
+
+extern void set_timer(int);
+extern int get_timer(void);
+extern int get_sec_ticks(void);
+
+#define TICKS_SEC get_sec_ticks()
diff --git a/pc-bios/s390-ccw/libnet/udp.c b/pc-bios/s390-ccw/libnet/udp.c
new file mode 100644
index 0000000..d6982ea
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/udp.c
@@ -0,0 +1,115 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+/************************ DEFINITIONS & DECLARATIONS *********************/
+
+#include <udp.h>
+#include <sys/socket.h>
+#include <dhcp.h>
+#include <dhcpv6.h>
+#include <dns.h>
+#include <tftp.h>
+
+
+/****************************** IMPLEMENTATION ***************************/
+
+
+/**
+ * NET: Handles UDP-packets according to Receive-handle diagram.
+ *
+ * @param  udp_packet UDP-packet to be handled
+ * @param  packetsize Length of the packet
+ * @return            ZERO - packet handled successfully;
+ *                    NON ZERO - packet was not handled (e.g. bad format)
+ * @see               receive_ether
+ * @see               udphdr
+ */
+int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize)
+{
+       struct udphdr * udph = (struct udphdr *) udp_packet;
+
+       if (packetsize < sizeof(struct udphdr))
+               return -1; // packet is too small
+
+       switch (htons(udph -> uh_dport)) {
+       case UDPPORT_BOOTPC:
+               if (udph -> uh_sport == htons(UDPPORT_BOOTPS))
+                       return handle_dhcp(fd, udp_packet + sizeof(struct 
udphdr),
+                                           packetsize - sizeof(struct udphdr));
+               else
+                       return -1;
+       case UDPPORT_DNSC:
+               if (udph -> uh_sport == htons(UDPPORT_DNSS))
+                       return handle_dns(udp_packet + sizeof(struct udphdr),
+                                         packetsize - sizeof(struct udphdr));
+               else
+                       return -1;
+       case UDPPORT_DHCPV6C:
+               return handle_dhcpv6(udp_packet+sizeof(struct udphdr),
+                                    packetsize - sizeof(struct udphdr));
+       case UDPPORT_TFTPC:
+               return handle_tftp(fd, udp_packet, packetsize);
+       default:
+               return -1;
+       }
+}
+
+/**
+ * NET: This function handles situation when "Destination unreachable"
+ *      ICMP-error occurs during sending UDP-packet.
+ *
+ * @param  err_code   Error Code (e.g. "Host unreachable")
+ * @param  packet     original UDP-packet
+ * @param  packetsize length of the packet
+ * @see               handle_icmp
+ */
+void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t 
err_code)
+{
+       struct udphdr * udph = (struct udphdr *) udp_packet;
+
+       if (packetsize < sizeof(struct udphdr))
+               return; // packet is too small
+
+       switch (htons(udph -> uh_sport)) {
+       case UDPPORT_TFTPC:
+               handle_tftp_dun(err_code);
+               break;
+       }
+}
+
+/**
+ * NET: Creates UDP-packet. Places UDP-header in a packet and fills it
+ *      with corresponding information.
+ *      <p>
+ *      Use this function with similar functions for other network layers
+ *      (fill_ethhdr, fill_iphdr, fill_dnshdr, fill_btphdr).
+ *
+ * @param  packet      Points to the place where UDP-header must be placed.
+ * @param  packetsize  Size of the packet in bytes incl. this hdr and data.
+ * @param  src_port    UDP source port
+ * @param  dest_port   UDP destination port
+ * @see                udphdr
+ * @see                fill_ethhdr
+ * @see                fill_iphdr
+ * @see                fill_dnshdr
+ * @see                fill_btphdr
+ */
+void fill_udphdr(uint8_t * packet, uint16_t packetsize,
+                uint16_t src_port, uint16_t dest_port)
+{
+       struct udphdr * udph = (struct udphdr *) packet;
+
+       udph -> uh_sport = htons(src_port);
+       udph -> uh_dport = htons(dest_port);
+       udph -> uh_ulen = htons(packetsize);
+       udph -> uh_sum = htons(0);
+}
diff --git a/pc-bios/s390-ccw/libnet/udp.h b/pc-bios/s390-ccw/libnet/udp.h
new file mode 100644
index 0000000..e716e04
--- /dev/null
+++ b/pc-bios/s390-ccw/libnet/udp.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * 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 _UDP_H
+#define _UDP_H
+
+#include <stdint.h>
+
+#define IPTYPE_UDP         17
+
+#define UDPPORT_BOOTPS     67   /**< UDP port of BootP/DHCP-server */
+#define UDPPORT_BOOTPC     68   /**< UDP port of BootP/DHCP-client */
+#define UDPPORT_DNSS       53   /**< UDP port of DNS-server        */
+#define UDPPORT_DNSC    32769   /**< UDP port of DNS-client        */
+#define UDPPORT_TFTPC    2001   /**< UDP port of TFTP-client      */
+#define UDPPORT_DHCPV6C   546   /**< UDP port of DHCPv6-client     */
+
+/** \struct udphdr
+ *  A header for UDP-packets.
+ *  For more information see RFC 768.
+ */
+struct udphdr {
+       uint16_t uh_sport;   /**< Source port                                  
*/
+       uint16_t uh_dport;   /**< Destinantion port                            
*/
+       uint16_t uh_ulen;    /**< Length in octets, incl. this header and data 
*/
+       uint16_t uh_sum;     /**< Checksum                                     
*/
+};
+typedef struct udphdr udp_hdr_t;
+
+typedef int32_t *(*handle_upper_udp_t)(uint8_t *, int32_t);
+typedef void    *(*handle_upper_udp_dun_t)(uint8_t);
+
+/* Handles UDP-packets that are detected by any network layer. */
+extern int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize);
+
+/* Handles UDP related ICMP-Dest.Unreachable packets that are detected by
+ * the network layers. */
+extern void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t 
err_code);
+
+/* fills udp header */
+extern void fill_udphdr(uint8_t *packet, uint16_t packetsize,
+                        uint16_t src_port, uint16_t dest_port);
+
+#endif
-- 
1.8.3.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]