All of lore.kernel.org
 help / color / mirror / Atom feed
From: Thomas Huth <thuth@redhat.com>
To: qemu-devel@nongnu.org, Christian Borntraeger <borntraeger@de.ibm.com>
Cc: Alexander Graf <agraf@suse.de>,
	Farhan Ali <alifm@linux.vnet.ibm.com>,
	David Hildenbrand <david@redhat.com>,
	Jens Freimann <jfreiman@redhat.com>,
	Eric Farman <farman@linux.vnet.ibm.com>
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	[thread overview]
Message-ID: <1498564100-10045-6-git-send-email-thuth@redhat.com> (raw)
In-Reply-To: <1498564100-10045-1-git-send-email-thuth@redhat.com>

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 <thuth@redhat.com>
---
 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:pass@www.host.org/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:pass@www.host.org/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

  parent reply	other threads:[~2017-06-27 11:49 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-06-27 11:48 [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 01/14] pc-bios/s390-ccw: Add the libc from the SLOF firmware Thomas Huth
2017-06-27 15:32   ` David Hildenbrand
2017-06-27 22:14     ` Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 02/14] pc-bios/s390-ccw: Start using the libc from SLOF Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 03/14] pc-bios/s390-ccw: Add a write() function for stdio Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 04/14] pc-bios/s390-ccw: Add implementation of sbrk() Thomas Huth
2017-06-27 11:48 ` Thomas Huth [this message]
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 06/14] libnet: Remove remainders of netsave code Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 07/14] libnet: Rework error message printing Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 08/14] libnet: Refactor some code of netload() into a separate function Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 09/14] pc-bios/s390-ccw: Make the basic libnet code compilable Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 10/14] pc-bios/s390-ccw: Add timer code for the libnet Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 11/14] pc-bios/s390-ccw: Add virtio-net driver code Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 12/14] pc-bios/s390-ccw: Load file via an intermediate .INS file Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 13/14] pc-bios/s390-ccw: Allow loading to address 0 Thomas Huth
2017-06-27 11:48 ` [Qemu-devel] [RFC PATCH 14/14] pc-bios/s390-ccw: Wire up the netload code Thomas Huth
2017-06-27 15:41 ` [Qemu-devel] [RFC PATCH 00/14] Implement network booting directly into the s390-ccw BIOS David Hildenbrand
2017-06-27 15:50 ` Viktor Mihajlovski
2017-06-27 21:40   ` Thomas Huth
2017-06-28  7:28     ` Viktor Mihajlovski
2017-06-28  8:02       ` Thomas Huth
2017-06-28 10:56         ` Thomas Huth
2017-06-28 15:02           ` Viktor Mihajlovski
2017-06-29  7:58             ` Thomas Huth
2017-06-29  8:10               ` Viktor Mihajlovski
2017-06-27 16:50 ` Farhan Ali
2017-06-28  7:34   ` Thomas Huth
2017-06-27 21:15 ` Alexander Graf
2017-06-27 21:56   ` Thomas Huth
2017-06-28  8:06     ` Gerd Hoffmann
2017-06-28  7:43 ` Christian Borntraeger
2017-06-28  8:59   ` Thomas Huth
2017-06-29  8:17     ` Thomas Huth
2017-06-29  8:39       ` Christian Borntraeger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1498564100-10045-6-git-send-email-thuth@redhat.com \
    --to=thuth@redhat.com \
    --cc=agraf@suse.de \
    --cc=alifm@linux.vnet.ibm.com \
    --cc=borntraeger@de.ibm.com \
    --cc=david@redhat.com \
    --cc=farman@linux.vnet.ibm.com \
    --cc=jfreiman@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.