qemu-devel.nongnu.org archive mirror
 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).