Index: uspace/srv/net/il/ip/ip.c
===================================================================
--- uspace/srv/net/il/ip/ip.c	(revision 02314f8abb5a1bda938d1dbd628a0f235d680055)
+++ uspace/srv/net/il/ip/ip.c	(revision c62ae1d6a041b4b49c507179774405fa77668074)
@@ -28,17 +28,15 @@
 
 /** @addtogroup ip
- * @{
+ *  @{
  */
 
 /** @file
- * IP module implementation.
- * @see arp.h
- */
-
-#include "ip.h"
-#include "ip_module.h"
+ *  IP module implementation.
+ *  @see arp.h
+ */
 
 #include <async.h>
 #include <errno.h>
+#include <err.h>
 #include <fibril_synch.h>
 #include <stdio.h>
@@ -53,8 +51,4 @@
 #include <byteorder.h>
 
-#include <adt/measured_strings.h>
-#include <adt/module_map.h>
-
-#include <packet_client.h>
 #include <net/socket_codes.h>
 #include <net/in.h>
@@ -73,185 +67,346 @@
 #include <ip_client.h>
 #include <ip_interface.h>
-#include <ip_header.h>
 #include <net_interface.h>
 #include <nil_interface.h>
 #include <tl_interface.h>
+#include <adt/measured_strings.h>
+#include <adt/module_map.h>
+#include <packet_client.h>
 #include <packet_remote.h>
 #include <il_local.h>
 
-/** IP module name. */
-#define NAME			"ip"
-
-/** IP version 4. */
-#define IPV4			4
-
-/** Default network interface IP version. */
+#include "ip.h"
+#include "ip_header.h"
+#include "ip_module.h"
+#include "ip_local.h"
+
+/** IP module name.
+ */
+#define NAME  "ip"
+
+/** IP version 4.
+ */
+#define IPV4				4
+
+/** Default network interface IP version.
+ */
 #define NET_DEFAULT_IPV		IPV4
 
-/** Default network interface IP routing. */
+/** Default network interface IP routing.
+ */
 #define NET_DEFAULT_IP_ROUTING	false
 
-/** Minimum IP packet content. */
-#define IP_MIN_CONTENT		576
-
-/** ARP module name. */
-#define ARP_NAME		"arp"
-
-/** ARP module filename. */
-#define ARP_FILENAME		"/srv/arp"
-
-/** IP packet address length. */
-#define IP_ADDR			sizeof(struct sockaddr_in6)
-
-/** IP packet prefix length. */
-#define IP_PREFIX		sizeof(ip_header_t)
-
-/** IP packet suffix length. */
-#define IP_SUFFIX		0
-
-/** IP packet maximum content length. */
-#define IP_MAX_CONTENT		65535
-
-/** The IP localhost address. */
+/** Minimum IP packet content.
+ */
+#define IP_MIN_CONTENT	576
+
+/** ARP module name.
+ */
+#define ARP_NAME				"arp"
+
+/** ARP module filename.
+ */
+#define ARP_FILENAME			"/srv/arp"
+
+/** IP packet address length.
+ */
+#define IP_ADDR							sizeof(struct sockaddr_in6)
+
+/** IP packet prefix length.
+ */
+#define IP_PREFIX						sizeof(ip_header_t)
+
+/** IP packet suffix length.
+ */
+#define IP_SUFFIX						0
+
+/** IP packet maximum content length.
+ */
+#define IP_MAX_CONTENT					65535
+
+/** The IP localhost address.
+ */
 #define IPV4_LOCALHOST_ADDRESS	htonl((127 << 24) + 1)
 
-/** IP global data. */
-ip_globals_t ip_globals;
-
-DEVICE_MAP_IMPLEMENT(ip_netifs, ip_netif_t);
-INT_MAP_IMPLEMENT(ip_protos, ip_proto_t);
-GENERIC_FIELD_IMPLEMENT(ip_routes, ip_route_t);
+/** IP global data.
+ */
+ip_globals_t	ip_globals;
+
+DEVICE_MAP_IMPLEMENT(ip_netifs, ip_netif_t)
+
+INT_MAP_IMPLEMENT(ip_protos, ip_proto_t)
+
+GENERIC_FIELD_IMPLEMENT(ip_routes, ip_route_t)
+
+/** Updates the device content length according to the new MTU value.
+ *  @param[in] device_id The device identifier.
+ *  @param[in] mtu The new mtu value.
+ *  @returns EOK on success.
+ *  @returns ENOENT if device is not found.
+ */
+int ip_mtu_changed_message(device_id_t device_id, size_t mtu);
+
+/** Updates the device state.
+ *  @param[in] device_id The device identifier.
+ *  @param[in] state The new state value.
+ *  @returns EOK on success.
+ *  @returns ENOENT if device is not found.
+ */
+int ip_device_state_message(device_id_t device_id, device_state_t state);
+
+/** Returns the device packet dimensions for sending.
+ *  @param[in] phone The service module phone.
+ *  @param[in] message The service specific message.
+ *  @param[in] device_id The device identifier.
+ *  @param[out] addr_len The minimum reserved address length.
+ *  @param[out] prefix The minimum reserved prefix size.
+ *  @param[out] content The maximum content size.
+ *  @param[out] suffix The minimum reserved suffix size.
+ *  @returns EOK on success.
+ */
+int ip_packet_size_message(device_id_t device_id, size_t * addr_len, size_t * prefix, size_t * content, size_t * suffix);
+
+/** Registers the transport layer protocol.
+ *  The traffic of this protocol will be supplied using either the receive function or IPC message.
+ *  @param[in] protocol The transport layer module protocol.
+ *  @param[in] service The transport layer module service.
+ *  @param[in] phone The transport layer module phone.
+ *  @param[in] tl_received_msg The receiving function.
+ *  @returns EOK on success.
+ *  @returns EINVAL if the protocol parameter and/or the service parameter is zero (0).
+ *  @returns EINVAL if the phone parameter is not a positive number and the tl_receive_msg is NULL.
+ *  @returns ENOMEM if there is not enough memory left.
+ */
+int ip_register(int protocol, services_t service, int phone, tl_received_msg_t tl_received_msg);
+
+/** Initializes a new network interface specific data.
+ *  Connects to the network interface layer module, reads the netif configuration, starts an ARP module if needed and sets the netif routing table.
+ *  The device identifier and the nil service has to be set.
+ *  @param[in,out] ip_netif Network interface specific data.
+ *  @returns EOK on success.
+ *  @returns ENOTSUP if DHCP is configured.
+ *  @returns ENOTSUP if IPv6 is configured.
+ *  @returns EINVAL if any of the addresses is invalid.
+ *  @returns EINVAL if the used ARP module is not known.
+ *  @returns ENOMEM if there is not enough memory left.
+ *  @returns Other error codes as defined for the net_get_device_conf_req() function.
+ *  @returns Other error codes as defined for the bind_service() function.
+ *  @returns Other error codes as defined for the specific arp_device_req() function.
+ *  @returns Other error codes as defined for the nil_packet_size_req() function.
+ */
+int ip_netif_initialize(ip_netif_ref ip_netif);
+
+/** Sends the packet or the packet queue via the specified route.
+ *  The ICMP_HOST_UNREACH error notification may be sent if route hardware destination address is found.
+ *  @param[in,out] packet The packet to be sent.
+ *  @param[in] netif The target network interface.
+ *  @param[in] route The target route.
+ *  @param[in] src The source address.
+ *  @param[in] dest The destination address.
+ *  @param[in] error The error module service.
+ *  @returns EOK on success.
+ *  @returns Other error codes as defined for the arp_translate_req() function.
+ *  @returns Other error codes as defined for the ip_prepare_packet() function.
+ */
+int ip_send_route(packet_t packet, ip_netif_ref netif, ip_route_ref route, in_addr_t * src, in_addr_t dest, services_t error);
+
+/** Prepares the outgoing packet or the packet queue.
+ *  The packet queue is a fragmented packet
+ *  Updates the first packet's IP header.
+ *  Prefixes the additional packets with fragment headers.
+ *  @param[in] source The source address.
+ *  @param[in] dest The destination address.
+ *  @param[in,out] packet The packet to be sent.
+ *  @param[in] destination The destination hardware address.
+ *  @returns EOK on success.
+ *  @returns EINVAL if the packet is too small to contain the IP header.
+ *  @returns EINVAL if the packet is too long than the IP allows.
+ *  @returns ENOMEM if there is not enough memory left.
+ *  @returns Other error codes as defined for the packet_set_addr() function.
+ */
+int ip_prepare_packet(in_addr_t * source, in_addr_t dest, packet_t packet, measured_string_ref destination);
+
+/** Checks the packet queue lengths and fragments the packets if needed.
+ *  The ICMP_FRAG_NEEDED error notification may be sent if the packet needs to be fragmented and the fragmentation is not allowed.
+ *  @param[in,out] packet The packet or the packet queue to be checked.
+ *  @param[in] prefix The minimum prefix size.
+ *  @param[in] content The maximum content size.
+ *  @param[in] suffix The minimum suffix size.
+ *  @param[in] addr_len The minimum address length.
+ *  @param[in] error The error module service.
+ *  @returns The packet or the packet queue of the allowed length.
+ *  @returns NULL if there are no packets left.
+ */
+packet_t ip_split_packet(packet_t packet, size_t prefix, size_t content, size_t suffix, socklen_t addr_len, services_t error);
+
+/** Checks the packet length and fragments it if needed.
+ *  The new fragments are queued before the original packet.
+ *  @param[in,out] packet The packet to be checked.
+ *  @param[in] length The maximum packet length.
+ *  @param[in] prefix The minimum prefix size.
+ *  @param[in] suffix The minimum suffix size.
+ *  @param[in] addr_len The minimum address length.
+ *  @returns EOK on success.
+ *  @returns EINVAL if the packet_get_addr() function fails.
+ *  @returns EINVAL if the packet does not contain the IP header.
+ *  @returns EPERM if the packet needs to be fragmented and the fragmentation is not allowed.
+ *  @returns ENOMEM if there is not enough memory left.
+ *  @returns ENOMEM if there is no packet available.
+ *  @returns ENOMEM if the packet is too small to contain the IP header.
+ *  @returns Other error codes as defined for the packet_trim() function.
+ *  @returns Other error codes as defined for the ip_create_middle_header() function.
+ *  @returns Other error codes as defined for the ip_fragment_packet_data() function.
+ */
+int ip_fragment_packet(packet_t packet, size_t length, size_t prefix, size_t suffix, socklen_t addr_len);
+
+/** Fragments the packet from the end.
+ *  @param[in] packet The packet to be fragmented.
+ *  @param[in,out] new_packet The new packet fragment.
+ *  @param[in,out] header The original packet header.
+ *  @param[in,out] new_header The new packet fragment header.
+ *  @param[in] length The new fragment length.
+ *  @param[in] src The source address.
+ *  @param[in] dest The destiantion address.
+ *  @param[in] addrlen The address length.
+ *  @returns EOK on success.
+ *  @returns ENOMEM if the target packet is too small.
+ *  @returns Other error codes as defined for the packet_set_addr() function.
+ *  @returns Other error codes as defined for the pq_insert_after() function.
+ */
+int ip_fragment_packet_data(packet_t packet, packet_t new_packet, ip_header_ref header, ip_header_ref new_header, size_t length, const struct sockaddr * src, const struct sockaddr * dest, socklen_t addrlen);
+
+/** Prefixes a middle fragment header based on the last fragment header to the packet.
+ *  @param[in] packet The packet to be prefixed.
+ *  @param[in] last The last header to be copied.
+ *  @returns The prefixed middle header.
+ *  @returns NULL on error.
+ */
+ip_header_ref ip_create_middle_header(packet_t packet, ip_header_ref last);
+
+/** Copies the fragment header.
+ *  Copies only the header itself and relevant IP options.
+ *  @param[out] last The created header.
+ *  @param[in] first The original header to be copied.
+ */
+void ip_create_last_header(ip_header_ref last, ip_header_ref first);
+
+/** Returns the network interface's IP address.
+ *  @param[in] netif The network interface.
+ *  @returns The IP address.
+ *  @returns NULL if no IP address was found.
+ */
+in_addr_t * ip_netif_address(ip_netif_ref netif);
+
+/** Searches all network interfaces if there is a suitable route.
+ *  @param[in] destination The destination address.
+ *  @returns The found route.
+ *  @returns NULL if no route was found.
+ */
+ip_route_ref ip_find_route(in_addr_t destination);
+
+/** Searches the network interfaces if there is a suitable route.
+ *  @param[in] netif The network interface to be searched for routes. May be NULL.
+ *  @param[in] destination The destination address.
+ *  @returns The found route.
+ *  @returns NULL if no route was found.
+ */
+ip_route_ref ip_netif_find_route(ip_netif_ref netif, in_addr_t destination);
+
+/** Processes the received IP packet or the packet queue one by one.
+ *  The packet is either passed to another module or released on error.
+ *  @param[in] device_id The source device identifier.
+ *  @param[in,out] packet The received packet.
+ *  @returns EOK on success and the packet is no longer needed.
+ *  @returns EINVAL if the packet is too small to carry the IP packet.
+ *  @returns EINVAL if the received address lengths differs from the registered values.
+ *  @returns ENOENT if the device is not found in the cache.
+ *  @returns ENOENT if the protocol for the device is not found in the cache.
+ *  @returns ENOMEM if there is not enough memory left.
+ */
+int ip_receive_message(device_id_t device_id, packet_t packet);
+
+/** Processes the received packet.
+ *  The packet is either passed to another module or released on error.
+ *  The ICMP_PARAM_POINTER error notification may be sent if the checksum is invalid.
+ *  The ICMP_EXC_TTL error notification may be sent if the TTL is less than two (2).
+ *  The ICMP_HOST_UNREACH error notification may be sent if no route was found.
+ *  The ICMP_HOST_UNREACH error notification may be sent if the packet is for another host and the routing is disabled.
+ *  @param[in] device_id The source device identifier.
+ *  @param[in] packet The received packet to be processed.
+ *  @returns EOK on success.
+ *  @returns EINVAL if the TTL is less than two (2).
+ *  @returns EINVAL if the checksum is invalid.
+ *  @returns EAFNOSUPPORT if the address family is not supported.
+ *  @returns ENOENT if no route was found.
+ *  @returns ENOENT if the packet is for another host and the routing is disabled.
+ */
+int ip_process_packet(device_id_t device_id, packet_t packet);
+
+/** Returns the packet destination address from the IP header.
+ *  @param[in] header The packet IP header to be read.
+ *  @returns The packet destination address.
+ */
+in_addr_t ip_get_destination(ip_header_ref header);
+
+/** Delivers the packet to the local host.
+ *  The packet is either passed to another module or released on error.
+ *  The ICMP_PROT_UNREACH error notification may be sent if the protocol is not found.
+ *  @param[in] device_id The source device identifier.
+ *  @param[in] packet The packet to be delivered.
+ *  @param[in] header The first packet IP header. May be NULL.
+ *  @param[in] error The packet error service.
+ *  @returns EOK on success.
+ *  @returns ENOTSUP if the packet is a fragment.
+ *  @returns EAFNOSUPPORT if the address family is not supported.
+ *  @returns ENOENT if the target protocol is not found.
+ *  @returns Other error codes as defined for the packet_set_addr() function.
+ *  @returns Other error codes as defined for the packet_trim() function.
+ *  @returns Other error codes as defined for the protocol specific tl_received_msg function.
+ */
+int ip_deliver_local(device_id_t device_id, packet_t packet, ip_header_ref header, services_t error);
+
+/** Prepares the ICMP notification packet.
+ *  Releases additional packets and keeps only the first one.
+ *  All packets is released on error.
+ *  @param[in] error The packet error service.
+ *  @param[in] packet The packet or the packet queue to be reported as faulty.
+ *  @param[in] header The first packet IP header. May be NULL.
+ *  @returns The found ICMP phone.
+ *  @returns EINVAL if the error parameter is set.
+ *  @returns EINVAL if the ICMP phone is not found.
+ *  @returns EINVAL if the ip_prepare_icmp() fails.
+ */
+int ip_prepare_icmp_and_get_phone(services_t error, packet_t packet, ip_header_ref header);
+
+/** Returns the ICMP phone.
+ *  Searches the registered protocols.
+ *  @returns The found ICMP phone.
+ *  @returns ENOENT if the ICMP is not registered.
+ */
+int ip_get_icmp_phone(void);
+
+/** Prepares the ICMP notification packet.
+ *  Releases additional packets and keeps only the first one.
+ *  @param[in] packet The packet or the packet queue to be reported as faulty.
+ *  @param[in] header The first packet IP header. May be NULL.
+ *  @returns EOK on success.
+ *  @returns EINVAL if there are no data in the packet.
+ *  @returns EINVAL if the packet is a fragment.
+ *  @returns ENOMEM if the packet is too short to contain the IP header.
+ *  @returns EAFNOSUPPORT if the address family is not supported.
+ *  @returns EPERM if the protocol is not allowed to send ICMP notifications. The ICMP protocol itself.
+ *  @returns Other error codes as defined for the packet_set_addr().
+ */
+int ip_prepare_icmp(packet_t packet, ip_header_ref header);
 
 /** Releases the packet and returns the result.
- *
- * @param[in] packet	The packet queue to be released.
- * @param[in] result	The result to be returned.
- * @return		The result parameter.
- */
-static int ip_release_and_return(packet_t packet, int result)
-{
-	pq_release_remote(ip_globals.net_phone, packet_get_id(packet));
-	return result;
-}
-
-/** Returns the ICMP phone.
- *
- * Searches the registered protocols.
- *
- * @returns		The found ICMP phone.
- * @returns		ENOENT if the ICMP is not registered.
- */
-static int ip_get_icmp_phone(void)
-{
-	ip_proto_ref proto;
-	int phone;
-
-	fibril_rwlock_read_lock(&ip_globals.protos_lock);
-	proto = ip_protos_find(&ip_globals.protos, IPPROTO_ICMP);
-	phone = proto ? proto->phone : ENOENT;
-	fibril_rwlock_read_unlock(&ip_globals.protos_lock);
-	return phone;
-}
-
-/** Prepares the ICMP notification packet.
- *
- * Releases additional packets and keeps only the first one.
- *
- * @param[in] packet	The packet or the packet queue to be reported as faulty.
- * @param[in] header	The first packet IP header. May be NULL.
- * @returns		EOK on success.
- * @returns		EINVAL if there are no data in the packet.
- * @returns		EINVAL if the packet is a fragment.
- * @returns		ENOMEM if the packet is too short to contain the IP
- *			header.
- * @returns		EAFNOSUPPORT if the address family is not supported.
- * @returns		EPERM if the protocol is not allowed to send ICMP
- *			notifications. The ICMP protocol itself.
- * @returns		Other error codes as defined for the packet_set_addr().
- */
-static int ip_prepare_icmp(packet_t packet, ip_header_ref header)
-{
-	packet_t next;
-	struct sockaddr *dest;
-	struct sockaddr_in dest_in;
-	socklen_t addrlen;
-
-	// detach the first packet and release the others
-	next = pq_detach(packet);
-	if (next)
-		pq_release_remote(ip_globals.net_phone, packet_get_id(next));
-
-	if (!header) {
-		if (packet_get_data_length(packet) <= sizeof(ip_header_t))
-			return ENOMEM;
-
-		// get header
-		header = (ip_header_ref) packet_get_data(packet);
-		if (!header)
-			return EINVAL;
-
-	}
-
-	// only for the first fragment
-	if (IP_FRAGMENT_OFFSET(header))
-		return EINVAL;
-
-	// not for the ICMP protocol
-	if (header->protocol == IPPROTO_ICMP)
-		return EPERM;
-
-	// set the destination address
-	switch (header->version) {
-	case IPVERSION:
-		addrlen = sizeof(dest_in);
-		bzero(&dest_in, addrlen);
-		dest_in.sin_family = AF_INET;
-		memcpy(&dest_in.sin_addr.s_addr, &header->source_address,
-		    sizeof(header->source_address));
-		dest = (struct sockaddr *) &dest_in;
-		break;
-
-	default:
-		return EAFNOSUPPORT;
-	}
-
-	return packet_set_addr(packet, NULL, (uint8_t *) dest, addrlen);
-}
-
-/** Prepares the ICMP notification packet.
- *
- * Releases additional packets and keeps only the first one.
- * All packets are released on error.
- *
- * @param[in] error	The packet error service.
- * @param[in] packet	The packet or the packet queue to be reported as faulty.
- * @param[in] header	The first packet IP header. May be NULL.
- * @returns		The found ICMP phone.
- * @returns		EINVAL if the error parameter is set.
- * @returns		EINVAL if the ICMP phone is not found.
- * @returns		EINVAL if the ip_prepare_icmp() fails.
- */
-static int
-ip_prepare_icmp_and_get_phone(services_t error, packet_t packet,
-    ip_header_ref header)
-{
-	int phone;
-
-	phone = ip_get_icmp_phone();
-	if (error || (phone < 0) || ip_prepare_icmp(packet, header))
-		return ip_release_and_return(packet, EINVAL);
-	return phone;
-}
-
-/** Initializes the IP module.
- *
- * @param[in] client_connection The client connection processing function. The
- *			module skeleton propagates its own one.
- * @returns		EOK on success.
- * @returns		ENOMEM if there is not enough memory left.
- */
-int ip_initialize(async_client_conn_t client_connection)
-{
-	int rc;
+ *  @param[in] packet The packet queue to be released.
+ *  @param[in] result The result to be returned.
+ *  @return The result parameter.
+ */
+int ip_release_and_return(packet_t packet, int result);
+
+int ip_initialize(async_client_conn_t client_connection){
+	ERROR_DECLARE;
 
 	fibril_rwlock_initialize(&ip_globals.lock);
@@ -264,951 +419,41 @@
 	ip_globals.gateway.gateway.s_addr = 0;
 	ip_globals.gateway.netif = NULL;
+	ERROR_PROPAGATE(ip_netifs_initialize(&ip_globals.netifs));
+	ERROR_PROPAGATE(ip_protos_initialize(&ip_globals.protos));
 	ip_globals.client_connection = client_connection;
-	
-	rc = ip_netifs_initialize(&ip_globals.netifs);
-	if (rc != EOK)
-		goto out;
-	rc = ip_protos_initialize(&ip_globals.protos);
-	if (rc != EOK)
-		goto out;
-	rc = modules_initialize(&ip_globals.modules);
-	if (rc != EOK)
-		goto out;
-	rc = add_module(NULL, &ip_globals.modules, ARP_NAME, ARP_FILENAME,
-	    SERVICE_ARP, 0, arp_connect_module);
-
-out:
+	ERROR_PROPAGATE(modules_initialize(&ip_globals.modules));
+	ERROR_PROPAGATE(add_module(NULL, &ip_globals.modules, ARP_NAME, ARP_FILENAME, SERVICE_ARP, 0, arp_connect_module));
 	fibril_rwlock_write_unlock(&ip_globals.lock);
-
-	return rc;
-}
-
-/** Initializes a new network interface specific data.
- *
- * Connects to the network interface layer module, reads the netif
- * configuration, starts an ARP module if needed and sets the netif routing
- * table.
- * 
- * The device identifier and the nil service has to be set.
- *
- * @param[in,out] ip_netif Network interface specific data.
- * @returns		EOK on success.
- * @returns		ENOTSUP if DHCP is configured.
- * @returns		ENOTSUP if IPv6 is configured.
- * @returns		EINVAL if any of the addresses is invalid.
- * @returns		EINVAL if the used ARP module is not known.
- * @returns		ENOMEM if there is not enough memory left.
- * @returns		Other error codes as defined for the
- *			net_get_device_conf_req() function.
- * @returns		Other error codes as defined for the bind_service()
- *			function.
- * @returns		Other error codes as defined for the specific
- *			arp_device_req() function.
- * @returns		Other error codes as defined for the
- *			nil_packet_size_req() function.
- */
-static int ip_netif_initialize(ip_netif_ref ip_netif)
-{
-	measured_string_t names[] = {
-		{
-			(char *) "IPV",
-			3
-		},
-		{
-			(char *) "IP_CONFIG",
-			9
-		},
-		{
-			(char *) "IP_ADDR",
-			7
-		},
-		{
-			(char *) "IP_NETMASK",
-			10
-		},
-		{
-			(char *) "IP_GATEWAY",
-			10
-		},
-		{
-			(char *) "IP_BROADCAST",
-			12
-		},
-		{
-			(char *) "ARP",
-			3
-		},
-		{
-			(char *) "IP_ROUTING",
-			10
-		}
-	};
-	measured_string_ref configuration;
-	size_t count = sizeof(names) / sizeof(measured_string_t);
-	char *data;
-	measured_string_t address;
-	ip_route_ref route;
-	in_addr_t gateway;
-	int index;
-	int rc;
-
-	ip_netif->arp = NULL;
-	route = NULL;
-	ip_netif->ipv = NET_DEFAULT_IPV;
-	ip_netif->dhcp = false;
-	ip_netif->routing = NET_DEFAULT_IP_ROUTING;
-	configuration = &names[0];
-
-	// get configuration
-	rc = net_get_device_conf_req(ip_globals.net_phone, ip_netif->device_id,
-	    &configuration, count, &data);
-	if (rc != EOK)
-		return rc;
-	
-	if (configuration) {
-		if (configuration[0].value)
-			ip_netif->ipv = strtol(configuration[0].value, NULL, 0);
-
-		ip_netif->dhcp = !str_lcmp(configuration[1].value, "dhcp",
-		    configuration[1].length);
-		
-		if (ip_netif->dhcp) {
-			// TODO dhcp
-			net_free_settings(configuration, data);
-			return ENOTSUP;
-		} else if (ip_netif->ipv == IPV4) {
-			route = (ip_route_ref) malloc(sizeof(ip_route_t));
-			if (!route) {
-				net_free_settings(configuration, data);
-				return ENOMEM;
-			}
-			route->address.s_addr = 0;
-			route->netmask.s_addr = 0;
-			route->gateway.s_addr = 0;
-			route->netif = ip_netif;
-			index = ip_routes_add(&ip_netif->routes, route);
-			if (index < 0) {
-				net_free_settings(configuration, data);
-				free(route);
-				return index;
-			}
-			
-			if ((inet_pton(AF_INET, configuration[2].value,
-			    (uint8_t *) &route->address.s_addr) != EOK) ||
-			    (inet_pton(AF_INET, configuration[3].value,
-			    (uint8_t *) &route->netmask.s_addr) != EOK) ||
-			    (inet_pton(AF_INET, configuration[4].value,
-			    (uint8_t *) &gateway.s_addr) == EINVAL) ||
-			    (inet_pton(AF_INET, configuration[5].value,
-			    (uint8_t *) &ip_netif->broadcast.s_addr) == EINVAL))
-			    {
-				net_free_settings(configuration, data);
-				return EINVAL;
-			}
-		} else {
-			// TODO ipv6 in separate module
-			net_free_settings(configuration, data);
-			return ENOTSUP;
-		}
-
-		if (configuration[6].value) {
-			ip_netif->arp = get_running_module(&ip_globals.modules,
-			    configuration[6].value);
-			if (!ip_netif->arp) {
-				printf("Failed to start the arp %s\n",
-				    configuration[6].value);
-				net_free_settings(configuration, data);
-				return EINVAL;
-			}
-		}
-		if (configuration[7].value)
-			ip_netif->routing = (configuration[7].value[0] == 'y');
-
-		net_free_settings(configuration, data);
-	}
-
-	// binds the netif service which also initializes the device
-	ip_netif->phone = nil_bind_service(ip_netif->service,
-	    (ipcarg_t) ip_netif->device_id, SERVICE_IP,
-	    ip_globals.client_connection);
-	if (ip_netif->phone < 0) {
-		printf("Failed to contact the nil service %d\n",
-		    ip_netif->service);
-		return ip_netif->phone;
-	}
-
-	// has to be after the device netif module initialization
-	if (ip_netif->arp) {
-		if (route) {
-			address.value = (char *) &route->address.s_addr;
-			address.length = CONVERT_SIZE(in_addr_t, char, 1);
-			
-			rc = arp_device_req(ip_netif->arp->phone,
-			    ip_netif->device_id, SERVICE_IP, ip_netif->service,
-			    &address);
-			if (rc != EOK)
-				return rc;
-		} else {
-			ip_netif->arp = 0;
-		}
-	}
-
-	// get packet dimensions
-	rc = nil_packet_size_req(ip_netif->phone, ip_netif->device_id,
-	    &ip_netif->packet_dimension);
-	if (rc != EOK)
-		return rc;
-	
-	if (ip_netif->packet_dimension.content < IP_MIN_CONTENT) {
-		printf("Maximum transmission unit %d bytes is too small, at "
-		    "least %d bytes are needed\n",
-		    ip_netif->packet_dimension.content, IP_MIN_CONTENT);
-		ip_netif->packet_dimension.content = IP_MIN_CONTENT;
-	}
-
-	index = ip_netifs_add(&ip_globals.netifs, ip_netif->device_id, ip_netif);
-	if (index < 0)
-		return index;
-	
-	if (gateway.s_addr) {
-		// the default gateway
-		ip_globals.gateway.address.s_addr = 0;
-		ip_globals.gateway.netmask.s_addr = 0;
-		ip_globals.gateway.gateway.s_addr = gateway.s_addr;
-		ip_globals.gateway.netif = ip_netif;
-	}
-
 	return EOK;
 }
 
-/** Updates the device content length according to the new MTU value.
- *
- * @param[in] device_id	The device identifier.
- * @param[in] mtu	The new mtu value.
- * @returns		EOK on success.
- * @returns		ENOENT if device is not found.
- */
-static int ip_mtu_changed_message(device_id_t device_id, size_t mtu)
-{
-	ip_netif_ref netif;
-
-	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
-	netif = ip_netifs_find(&ip_globals.netifs, device_id);
-	if (!netif) {
-		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-		return ENOENT;
-	}
-	netif->packet_dimension.content = mtu;
-	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-
-	printf("%s: Device %d changed MTU to %d\n", NAME, device_id, mtu);
-
-	return EOK;
-}
-
-/** Updates the device state.
- *
- * @param[in] device_id	The device identifier.
- * @param[in] state	The new state value.
- * @returns		EOK on success.
- * @returns		ENOENT if device is not found.
- */
-static int ip_device_state_message(device_id_t device_id, device_state_t state)
-{
-	ip_netif_ref netif;
-
-	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
-	// find the device
-	netif = ip_netifs_find(&ip_globals.netifs, device_id);
-	if (!netif) {
-		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-		return ENOENT;
-	}
-	netif->state = state;
-	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-
-	printf("%s: Device %d changed state to %d\n", NAME, device_id, state);
-
-	return EOK;
-}
-
-
-/** Prefixes a middle fragment header based on the last fragment header to the
- * packet.
- *
- * @param[in] packet	The packet to be prefixed.
- * @param[in] last	The last header to be copied.
- * @returns		The prefixed middle header.
- * @returns		NULL on error.
- */
-static ip_header_ref
-ip_create_middle_header(packet_t packet, ip_header_ref last)
-{
-	ip_header_ref middle;
-
-	middle = (ip_header_ref) packet_suffix(packet, IP_HEADER_LENGTH(last));
-	if (!middle)
-		return NULL;
-	memcpy(middle, last, IP_HEADER_LENGTH(last));
-	middle->flags |= IPFLAG_MORE_FRAGMENTS;
-	return middle;
-}
-
-/** Copies the fragment header.
- *
- * Copies only the header itself and relevant IP options.
- *
- * @param[out] last	The created header.
- * @param[in] first	The original header to be copied.
- */
-static void ip_create_last_header(ip_header_ref last, ip_header_ref first)
-{
-	ip_option_ref option;
-	size_t next;
-	size_t length;
-
-	// copy first itself
-	memcpy(last, first, sizeof(ip_header_t));
-	length = sizeof(ip_header_t);
-	next = sizeof(ip_header_t);
-
-	// process all ip options
-	while (next < first->header_length) {
-		option = (ip_option_ref) (((uint8_t *) first) + next);
-		// skip end or noop
-		if ((option->type == IPOPT_END) ||
-		    (option->type == IPOPT_NOOP)) {
-			next++;
-		} else {
-			// copy if told so or skip
-			if (IPOPT_COPIED(option->type)) {
-				memcpy(((uint8_t *) last) + length,
-				    ((uint8_t *) first) + next, option->length);
-				length += option->length;
-			}
-			// next option
-			next += option->length;
-		}
-	}
-
-	// align 4 byte boundary
-	if (length % 4) {
-		bzero(((uint8_t *) last) + length, 4 - (length % 4));
-		last->header_length = length / 4 + 1;
-	} else {
-		last->header_length = length / 4;
-	}
-
-	last->header_checksum = 0;
-}
-
-/** Prepares the outgoing packet or the packet queue.
- *
- * The packet queue is a fragmented packet
- * Updates the first packet's IP header.
- * Prefixes the additional packets with fragment headers.
- *
- * @param[in] source	The source address.
- * @param[in] dest	The destination address.
- * @param[in,out] packet The packet to be sent.
- * @param[in] destination The destination hardware address.
- * @returns		EOK on success.
- * @returns		EINVAL if the packet is too small to contain the IP
- *			header.
- * @returns		EINVAL if the packet is too long than the IP allows.
- * @returns		ENOMEM if there is not enough memory left.
- * @returns		Other error codes as defined for the packet_set_addr()
- *			function.
- */
-static int
-ip_prepare_packet(in_addr_t *source, in_addr_t dest, packet_t packet,
-    measured_string_ref destination)
-{
-	size_t length;
-	ip_header_ref header;
-	ip_header_ref last_header;
-	ip_header_ref middle_header;
-	packet_t next;
-	int rc;
-
-	length = packet_get_data_length(packet);
-	if ((length < sizeof(ip_header_t)) || (length > IP_MAX_CONTENT))
-		return EINVAL;
-
-	header = (ip_header_ref) packet_get_data(packet);
-	if (destination) {
-		rc = packet_set_addr(packet, NULL, (uint8_t *) destination->value,
-		    CONVERT_SIZE(char, uint8_t, destination->length));
-	} else {
-		rc = packet_set_addr(packet, NULL, NULL, 0);
-	}
-	if (rc != EOK)
-		return rc;
-	
-	header->version = IPV4;
-	header->fragment_offset_high = 0;
-	header->fragment_offset_low = 0;
-	header->header_checksum = 0;
-	if (source)
-		header->source_address = source->s_addr;
-	header->destination_address = dest.s_addr;
-
-	fibril_rwlock_write_lock(&ip_globals.lock);
-	ip_globals.packet_counter++;
-	header->identification = htons(ip_globals.packet_counter);
-	fibril_rwlock_write_unlock(&ip_globals.lock);
-
-	if (pq_next(packet)) {
-		last_header = (ip_header_ref) malloc(IP_HEADER_LENGTH(header));
-		if (!last_header)
-			return ENOMEM;
-		ip_create_last_header(last_header, header);
-		next = pq_next(packet);
-		while (pq_next(next)) {
-			middle_header = (ip_header_ref) packet_prefix(next,
-			    IP_HEADER_LENGTH(last_header));
-			if (!middle_header) {
-				free(last_header);
-				return ENOMEM;
-			}
-
-			memcpy(middle_header, last_header,
-			    IP_HEADER_LENGTH(last_header));
-			header->flags |= IPFLAG_MORE_FRAGMENTS;
-			middle_header->total_length =
-			    htons(packet_get_data_length(next));
-			middle_header->fragment_offset_high =
-			    IP_COMPUTE_FRAGMENT_OFFSET_HIGH(length);
-			middle_header->fragment_offset_low =
-			    IP_COMPUTE_FRAGMENT_OFFSET_LOW(length);
-			middle_header->header_checksum =
-			    IP_HEADER_CHECKSUM(middle_header);
-			if (destination) {
-				rc = packet_set_addr(next, NULL,
-				    (uint8_t *) destination->value,
-				    CONVERT_SIZE(char, uint8_t,
-				    destination->length));
-				if (rc != EOK) {
-				    	free(last_header);
-					return rc;
-				}
-			}
-			length += packet_get_data_length(next);
-			next = pq_next(next);
-		}
-
-		middle_header = (ip_header_ref) packet_prefix(next,
-		    IP_HEADER_LENGTH(last_header));
-		if (!middle_header) {
-			free(last_header);
-			return ENOMEM;
-		}
-
-		memcpy(middle_header, last_header,
-		    IP_HEADER_LENGTH(last_header));
-		middle_header->total_length =
-		    htons(packet_get_data_length(next));
-		middle_header->fragment_offset_high =
-		    IP_COMPUTE_FRAGMENT_OFFSET_HIGH(length);
-		middle_header->fragment_offset_low =
-		    IP_COMPUTE_FRAGMENT_OFFSET_LOW(length);
-		middle_header->header_checksum =
-		    IP_HEADER_CHECKSUM(middle_header);
-		if (destination) {
-			rc = packet_set_addr(next, NULL,
-			    (uint8_t *) destination->value,
-			    CONVERT_SIZE(char, uint8_t, destination->length));
-			if (rc != EOK) {
-				free(last_header);
-				return rc;
-			}
-		}
-		length += packet_get_data_length(next);
-		free(last_header);
-		header->flags |= IPFLAG_MORE_FRAGMENTS;
-	}
-
-	header->total_length = htons(length);
-	// unnecessary for all protocols
-	header->header_checksum = IP_HEADER_CHECKSUM(header);
-
-	return EOK;
-}
-
-/** Fragments the packet from the end.
- *
- * @param[in] packet	The packet to be fragmented.
- * @param[in,out] new_packet The new packet fragment.
- * @param[in,out] header The original packet header.
- * @param[in,out] new_header The new packet fragment header.
- * @param[in] length	The new fragment length.
- * @param[in] src	The source address.
- * @param[in] dest	The destiantion address.
- * @param[in] addrlen	The address length.
- * @returns		EOK on success.
- * @returns		ENOMEM if the target packet is too small.
- * @returns		Other error codes as defined for the packet_set_addr()
- *			function.
- * @returns		Other error codes as defined for the pq_insert_after()
- *			function.
- */
-static int
-ip_fragment_packet_data(packet_t packet, packet_t new_packet,
-    ip_header_ref header, ip_header_ref new_header, size_t length,
-    const struct sockaddr *src, const struct sockaddr *dest, socklen_t addrlen)
-{
-	void *data;
-	size_t offset;
-	int rc;
-
-	data = packet_suffix(new_packet, length);
-	if (!data)
-		return ENOMEM;
-
-	memcpy(data, ((void *) header) + IP_TOTAL_LENGTH(header) - length,
-	    length);
-	
-	rc = packet_trim(packet, 0, length);
-	if (rc != EOK)
-		return rc;
-	
-	header->total_length = htons(IP_TOTAL_LENGTH(header) - length);
-	new_header->total_length = htons(IP_HEADER_LENGTH(new_header) + length);
-	offset = IP_FRAGMENT_OFFSET(header) + IP_HEADER_DATA_LENGTH(header);
-	new_header->fragment_offset_high =
-	    IP_COMPUTE_FRAGMENT_OFFSET_HIGH(offset);
-	new_header->fragment_offset_low =
-	    IP_COMPUTE_FRAGMENT_OFFSET_LOW(offset);
-	new_header->header_checksum = IP_HEADER_CHECKSUM(new_header);
-	
-	rc = packet_set_addr(new_packet, (const uint8_t *) src,
-	    (const uint8_t *) dest, addrlen);
-	if (rc != EOK)
-		return rc;
-
-	return pq_insert_after(packet, new_packet);
-}
-
-/** Checks the packet length and fragments it if needed.
- *
- * The new fragments are queued before the original packet.
- *
- * @param[in,out] packet The packet to be checked.
- * @param[in] length	The maximum packet length.
- * @param[in] prefix	The minimum prefix size.
- * @param[in] suffix	The minimum suffix size.
- * @param[in] addr_len	The minimum address length.
- * @returns		EOK on success.
- * @returns		EINVAL if the packet_get_addr() function fails.
- * @returns		EINVAL if the packet does not contain the IP header.
- * @returns		EPERM if the packet needs to be fragmented and the
- *			fragmentation is not allowed.
- * @returns		ENOMEM if there is not enough memory left.
- * @returns		ENOMEM if there is no packet available.
- * @returns		ENOMEM if the packet is too small to contain the IP
- *			header.
- * @returns		Other error codes as defined for the packet_trim()
- *			function.
- * @returns		Other error codes as defined for the
- *			ip_create_middle_header() function.
- * @returns		Other error codes as defined for the
- *			ip_fragment_packet_data() function.
- */
-static int
-ip_fragment_packet(packet_t packet, size_t length, size_t prefix, size_t suffix,
-    socklen_t addr_len)
-{
-	packet_t new_packet;
-	ip_header_ref header;
-	ip_header_ref middle_header;
-	ip_header_ref last_header;
-	struct sockaddr *src;
-	struct sockaddr *dest;
-	socklen_t addrlen;
-	int result;
-	int rc;
-
-	result = packet_get_addr(packet, (uint8_t **) &src, (uint8_t **) &dest);
-	if (result <= 0)
-		return EINVAL;
-
-	addrlen = (socklen_t) result;
-	if (packet_get_data_length(packet) <= sizeof(ip_header_t))
-		return ENOMEM;
-
-	// get header
-	header = (ip_header_ref) packet_get_data(packet);
-	if (!header)
-		return EINVAL;
-
-	// fragmentation forbidden?
-	if(header->flags & IPFLAG_DONT_FRAGMENT)
-		return EPERM;
-
-	// create the last fragment
-	new_packet = packet_get_4_remote(ip_globals.net_phone, prefix, length,
-	    suffix, ((addrlen > addr_len) ? addrlen : addr_len));
-	if (!new_packet)
-		return ENOMEM;
-
-	// allocate as much as originally
-	last_header = (ip_header_ref) packet_suffix(new_packet,
-	    IP_HEADER_LENGTH(header));
-	if (!last_header)
-		return ip_release_and_return(packet, ENOMEM);
-
-	ip_create_last_header(last_header, header);
-
-	// trim the unused space
-	rc = packet_trim(new_packet, 0,
-	    IP_HEADER_LENGTH(header) - IP_HEADER_LENGTH(last_header));
-	if (rc != EOK)
-		return ip_release_and_return(packet, rc);
-
-	// biggest multiple of 8 lower than content
-	// TODO even fragmentation?
-	length = length & ~0x7;
-	
-	rc = ip_fragment_packet_data(packet, new_packet, header, last_header,
-	    ((IP_HEADER_DATA_LENGTH(header) -
-	    ((length - IP_HEADER_LENGTH(header)) & ~0x7)) %
-	    ((length - IP_HEADER_LENGTH(last_header)) & ~0x7)),
-	    src, dest, addrlen);
-	if (rc != EOK)
-		return ip_release_and_return(packet, rc);
-
-	// mark the first as fragmented
-	header->flags |= IPFLAG_MORE_FRAGMENTS;
-
-	// create middle framgents
-	while (IP_TOTAL_LENGTH(header) > length) {
-		new_packet = packet_get_4_remote(ip_globals.net_phone, prefix,
-		    length, suffix,
-		    ((addrlen >= addr_len) ? addrlen : addr_len));
-		if (!new_packet)
-			return ENOMEM;
-
-		middle_header = ip_create_middle_header(new_packet,
-		    last_header);
-		if (!middle_header)
-			return ip_release_and_return(packet, ENOMEM);
-
-		rc = ip_fragment_packet_data(packet, new_packet, header,
-		    middle_header,
-		    (length - IP_HEADER_LENGTH(middle_header)) & ~0x7,
-		    src, dest, addrlen);
-		if (rc != EOK)
-			return ip_release_and_return(packet, rc);
-	}
-
-	// finish the first fragment
-	header->header_checksum = IP_HEADER_CHECKSUM(header);
-
-	return EOK;
-}
-
-/** Checks the packet queue lengths and fragments the packets if needed.
- *
- * The ICMP_FRAG_NEEDED error notification may be sent if the packet needs to
- * be fragmented and the fragmentation is not allowed.
- *
- * @param[in,out] packet The packet or the packet queue to be checked.
- * @param[in] prefix	The minimum prefix size.
- * @param[in] content	The maximum content size.
- * @param[in] suffix	The minimum suffix size.
- * @param[in] addr_len	The minimum address length.
- * @param[in] error	The error module service.
- * @returns		The packet or the packet queue of the allowed length.
- * @returns		NULL if there are no packets left.
- */
-static packet_t
-ip_split_packet(packet_t packet, size_t prefix, size_t content, size_t suffix,
-    socklen_t addr_len, services_t error)
-{
-	size_t length;
-	packet_t next;
-	packet_t new_packet;
-	int result;
-	int phone;
-
-	next = packet;
-	// check all packets
-	while (next) {
-		length = packet_get_data_length(next);
-		
-		if (length <= content) {
-			next = pq_next(next);
-			continue;
-		}
-
-		// too long
-		result = ip_fragment_packet(next, content, prefix,
-		    suffix, addr_len);
-		if (result != EOK) {
-			new_packet = pq_detach(next);
-			if (next == packet) {
-				// the new first packet of the queue
-				packet = new_packet;
-			}
-			// fragmentation needed?
-			if (result == EPERM) {
-				phone = ip_prepare_icmp_and_get_phone(
-				    error, next, NULL);
-				if (phone >= 0) {
-					// fragmentation necessary ICMP
-					icmp_destination_unreachable_msg(phone,
-					    ICMP_FRAG_NEEDED, content, next);
-				}
-			} else {
-				pq_release_remote(ip_globals.net_phone,
-				    packet_get_id(next));
-			}
-
-			next = new_packet;
-			continue;
-		}
-
-		next = pq_next(next);
-	}
-
-	return packet;
-}
-
-/** Sends the packet or the packet queue via the specified route.
- *
- * The ICMP_HOST_UNREACH error notification may be sent if route hardware
- * destination address is found.
- *
- * @param[in,out] packet The packet to be sent.
- * @param[in] netif	The target network interface.
- * @param[in] route	The target route.
- * @param[in] src	The source address.
- * @param[in] dest	The destination address.
- * @param[in] error	The error module service.
- * @returns		EOK on success.
- * @returns		Other error codes as defined for the arp_translate_req()
- *			function.
- * @returns		Other error codes as defined for the ip_prepare_packet()
- *			function.
- */
-static int
-ip_send_route(packet_t packet, ip_netif_ref netif, ip_route_ref route,
-    in_addr_t *src, in_addr_t dest, services_t error)
-{
-	measured_string_t destination;
-	measured_string_ref translation;
-	char *data;
-	int phone;
-	int rc;
-
-	// get destination hardware address
-	if (netif->arp && (route->address.s_addr != dest.s_addr)) {
-		destination.value = route->gateway.s_addr ?
-		    (char *) &route->gateway.s_addr : (char *) &dest.s_addr;
-		destination.length = CONVERT_SIZE(dest.s_addr, char, 1);
-
-		rc = arp_translate_req(netif->arp->phone, netif->device_id,
-		    SERVICE_IP, &destination, &translation, &data);
-		if (rc != EOK) {
-			pq_release_remote(ip_globals.net_phone,
-			    packet_get_id(packet));
-			return rc;
-		}
-
-		if (!translation || !translation->value) {
-			if (translation) {
-				free(translation);
-				free(data);
-			}
-			phone = ip_prepare_icmp_and_get_phone(error, packet,
-			    NULL);
-			if (phone >= 0) {
-				// unreachable ICMP if no routing
-				icmp_destination_unreachable_msg(phone,
-				    ICMP_HOST_UNREACH, 0, packet);
-			}
-			return EINVAL;
-		}
-
-	} else {
-		translation = NULL;
-	}
-
-	rc = ip_prepare_packet(src, dest, packet, translation);
-	if (rc != EOK) {
-		pq_release_remote(ip_globals.net_phone, packet_get_id(packet));
-	} else {
-		packet = ip_split_packet(packet, netif->packet_dimension.prefix,
-		    netif->packet_dimension.content,
-		    netif->packet_dimension.suffix,
-		    netif->packet_dimension.addr_len, error);
-		if (packet) {
-			nil_send_msg(netif->phone, netif->device_id, packet,
-			    SERVICE_IP);
-		}
-	}
-
-	if (translation) {
-		free(translation);
-		free(data);
-	}
-
-	return rc;
-}
-
-/** Searches the network interfaces if there is a suitable route.
- *
- * @param[in] netif	The network interface to be searched for routes. May be
- *			NULL.
- * @param[in] destination The destination address.
- * @returns		The found route.
- * @returns		NULL if no route was found.
- */
-static ip_route_ref
-ip_netif_find_route(ip_netif_ref netif, in_addr_t destination)
-{
-	int index;
-	ip_route_ref route;
-
-	if (!netif)
-		return NULL;
-
-	// start with the first one - the direct route
-	for (index = 0; index < ip_routes_count(&netif->routes); index++) {
-		route = ip_routes_get_index(&netif->routes, index);
-		if (route &&
-		    ((route->address.s_addr & route->netmask.s_addr) ==
-		    (destination.s_addr & route->netmask.s_addr))) {
-			return route;
-		}
-	}
-
-	return NULL;
-}
-
-/** Searches all network interfaces if there is a suitable route.
- *
- * @param[in] destination The destination address.
- * @returns		The found route.
- * @returns		NULL if no route was found.
- */
-static ip_route_ref ip_find_route(in_addr_t destination) {
-	int index;
-	ip_route_ref route;
-	ip_netif_ref netif;
-
-	// start with the last netif - the newest one
-	index = ip_netifs_count(&ip_globals.netifs) - 1;
-	while (index >= 0) {
-		netif = ip_netifs_get_index(&ip_globals.netifs, index);
-		if (netif && (netif->state == NETIF_ACTIVE)) {
-			route = ip_netif_find_route(netif, destination);
-			if (route)
-				return route;
-		}
-		index--;
-	}
-
-	return &ip_globals.gateway;
-}
-
-/** Returns the network interface's IP address.
- *
- * @param[in] netif	The network interface.
- * @returns		The IP address.
- * @returns		NULL if no IP address was found.
- */
-static in_addr_t *ip_netif_address(ip_netif_ref netif)
-{
-	ip_route_ref route;
-
-	route = ip_routes_get_index(&netif->routes, 0);
-	return route ? &route->address : NULL;
-}
-
-/** Registers the transport layer protocol.
- *
- * The traffic of this protocol will be supplied using either the receive
- * function or IPC message.
- *
- * @param[in] protocol	The transport layer module protocol.
- * @param[in] service	The transport layer module service.
- * @param[in] phone	The transport layer module phone.
- * @param[in] received_msg The receiving function.
- * @returns		EOK on success.
- * @returns		EINVAL if the protocol parameter and/or the service
- *			parameter is zero.
- * @returns		EINVAL if the phone parameter is not a positive number
- *			and the tl_receive_msg is NULL.
- * @returns		ENOMEM if there is not enough memory left.
- */
-static int
-ip_register(int protocol, services_t service, int phone,
-    tl_received_msg_t received_msg)
-{
-	ip_proto_ref proto;
-	int index;
-
-	if (!protocol || !service || ((phone < 0) && !received_msg))
-		return EINVAL;
-
-	proto = (ip_proto_ref) malloc(sizeof(ip_protos_t));
-	if (!proto)
-		return ENOMEM;
-
-	proto->protocol = protocol;
-	proto->service = service;
-	proto->phone = phone;
-	proto->received_msg = received_msg;
-
-	fibril_rwlock_write_lock(&ip_globals.protos_lock);
-	index = ip_protos_add(&ip_globals.protos, proto->protocol, proto);
-	if (index < 0) {
-		fibril_rwlock_write_unlock(&ip_globals.protos_lock);
-		free(proto);
-		return index;
-	}
-	fibril_rwlock_write_unlock(&ip_globals.protos_lock);
-
-	printf("%s: Protocol registered (protocol: %d, phone: %d)\n",
-	    NAME, proto->protocol, proto->phone);
-
-	return EOK;
-}
-
-static int
-ip_device_req_local(int il_phone, device_id_t device_id, services_t netif)
-{
+int ip_device_req_local(int il_phone, device_id_t device_id, services_t netif){
+	ERROR_DECLARE;
+
 	ip_netif_ref ip_netif;
 	ip_route_ref route;
 	int index;
-	int rc;
 
 	ip_netif = (ip_netif_ref) malloc(sizeof(ip_netif_t));
-	if (!ip_netif)
+	if(! ip_netif){
 		return ENOMEM;
-
-	rc = ip_routes_initialize(&ip_netif->routes);
-	if (rc != EOK) {
+	}
+	if(ERROR_OCCURRED(ip_routes_initialize(&ip_netif->routes))){
 		free(ip_netif);
-		return rc;
-	}
-
+		return ERROR_CODE;
+	}
 	ip_netif->device_id = device_id;
 	ip_netif->service = netif;
 	ip_netif->state = NETIF_STOPPED;
-
 	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
-
-	rc = ip_netif_initialize(ip_netif);
-	if (rc != EOK) {
+	if(ERROR_OCCURRED(ip_netif_initialize(ip_netif))){
 		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
 		ip_routes_destroy(&ip_netif->routes);
 		free(ip_netif);
-		return rc;
-	}
-	if (ip_netif->arp)
-		ip_netif->arp->usage++;
-
+		return ERROR_CODE;
+	}
+	if(ip_netif->arp){
+		++ ip_netif->arp->usage;
+	}
 	// print the settings
 	printf("%s: Device registered (id: %d, phone: %d, ipv: %d, conf: %s)\n",
@@ -1222,672 +467,429 @@
 	char gateway[INET_ADDRSTRLEN];
 	
-	for (index = 0; index < ip_routes_count(&ip_netif->routes); index++) {
+	for (index = 0; index < ip_routes_count(&ip_netif->routes); ++ index){
 		route = ip_routes_get_index(&ip_netif->routes, index);
 		if (route) {
-			inet_ntop(AF_INET, (uint8_t *) &route->address.s_addr,
-			    address, INET_ADDRSTRLEN);
-			inet_ntop(AF_INET, (uint8_t *) &route->netmask.s_addr,
-			    netmask, INET_ADDRSTRLEN);
-			inet_ntop(AF_INET, (uint8_t *) &route->gateway.s_addr,
-			    gateway, INET_ADDRSTRLEN);
-			printf("%s: Route %d (address: %s, netmask: %s, "
-			    "gateway: %s)\n", NAME, index, address, netmask,
-			    gateway);
+			inet_ntop(AF_INET, (uint8_t *) &route->address.s_addr, address, INET_ADDRSTRLEN);
+			inet_ntop(AF_INET, (uint8_t *) &route->netmask.s_addr, netmask, INET_ADDRSTRLEN);
+			inet_ntop(AF_INET, (uint8_t *) &route->gateway.s_addr, gateway, INET_ADDRSTRLEN);
+			printf("%s: Route %d (address: %s, netmask: %s, gateway: %s)\n",
+			    NAME, index, address, netmask, gateway);
 		}
 	}
 	
-	inet_ntop(AF_INET, (uint8_t *) &ip_netif->broadcast.s_addr, address,
-	    INET_ADDRSTRLEN);
+	inet_ntop(AF_INET, (uint8_t *) &ip_netif->broadcast.s_addr, address, INET_ADDRSTRLEN);
+	printf("%s: Broadcast (%s)\n", NAME, address);
+	
 	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-
-	printf("%s: Broadcast (%s)\n", NAME, address);
-
 	return EOK;
 }
 
-static int
-ip_send_msg_local(int il_phone, device_id_t device_id, packet_t packet,
-    services_t sender, services_t error)
-{
+int ip_netif_initialize(ip_netif_ref ip_netif){
+	ERROR_DECLARE;
+
+	measured_string_t names[] = {{str_dup("IPV"), 3}, {str_dup("IP_CONFIG"), 9}, {str_dup("IP_ADDR"), 7}, {str_dup("IP_NETMASK"), 10}, {str_dup("IP_GATEWAY"), 10}, {str_dup("IP_BROADCAST"), 12}, {str_dup("ARP"), 3}, {str_dup("IP_ROUTING"), 10}};
+	measured_string_ref configuration;
+	size_t count = sizeof(names) / sizeof(measured_string_t);
+	char * data;
+	measured_string_t address;
+	int index;
+	ip_route_ref route;
+	in_addr_t gateway;
+
+	ip_netif->arp = NULL;
+	route = NULL;
+	ip_netif->ipv = NET_DEFAULT_IPV;
+	ip_netif->dhcp = false;
+	ip_netif->routing = NET_DEFAULT_IP_ROUTING;
+	configuration = &names[0];
+	// get configuration
+	ERROR_PROPAGATE(net_get_device_conf_req(ip_globals.net_phone, ip_netif->device_id, &configuration, count, &data));
+	if(configuration){
+		if(configuration[0].value){
+			ip_netif->ipv = strtol(configuration[0].value, NULL, 0);
+		}
+		ip_netif->dhcp = ! str_lcmp(configuration[1].value, "dhcp", configuration[1].length);
+		if(ip_netif->dhcp){
+			// TODO dhcp
+			net_free_settings(configuration, data);
+			return ENOTSUP;
+		}else if(ip_netif->ipv == IPV4){
+			route = (ip_route_ref) malloc(sizeof(ip_route_t));
+			if(! route){
+				net_free_settings(configuration, data);
+				return ENOMEM;
+			}
+			route->address.s_addr = 0;
+			route->netmask.s_addr = 0;
+			route->gateway.s_addr = 0;
+			route->netif = ip_netif;
+			index = ip_routes_add(&ip_netif->routes, route);
+			if(index < 0){
+				net_free_settings(configuration, data);
+				free(route);
+				return index;
+			}
+			if(ERROR_OCCURRED(inet_pton(AF_INET, configuration[2].value, (uint8_t *) &route->address.s_addr))
+				|| ERROR_OCCURRED(inet_pton(AF_INET, configuration[3].value, (uint8_t *) &route->netmask.s_addr))
+				|| (inet_pton(AF_INET, configuration[4].value, (uint8_t *) &gateway.s_addr) == EINVAL)
+				|| (inet_pton(AF_INET, configuration[5].value, (uint8_t *) &ip_netif->broadcast.s_addr) == EINVAL)){
+				net_free_settings(configuration, data);
+				return EINVAL;
+			}
+		}else{
+			// TODO ipv6 in separate module
+			net_free_settings(configuration, data);
+			return ENOTSUP;
+		}
+		if(configuration[6].value){
+			ip_netif->arp = get_running_module(&ip_globals.modules, configuration[6].value);
+			if(! ip_netif->arp){
+				printf("Failed to start the arp %s\n", configuration[6].value);
+				net_free_settings(configuration, data);
+				return EINVAL;
+			}
+		}
+		if(configuration[7].value){
+			ip_netif->routing = (configuration[7].value[0] == 'y');
+		}
+		net_free_settings(configuration, data);
+	}
+	// binds the netif service which also initializes the device
+	ip_netif->phone = nil_bind_service(ip_netif->service, (ipcarg_t) ip_netif->device_id, SERVICE_IP, ip_globals.client_connection);
+	if(ip_netif->phone < 0){
+		printf("Failed to contact the nil service %d\n", ip_netif->service);
+		return ip_netif->phone;
+	}
+	// has to be after the device netif module initialization
+	if(ip_netif->arp){
+		if(route){
+			address.value = (char *) &route->address.s_addr;
+			address.length = CONVERT_SIZE(in_addr_t, char, 1);
+			ERROR_PROPAGATE(arp_device_req(ip_netif->arp->phone, ip_netif->device_id, SERVICE_IP, ip_netif->service, &address));
+		}else{
+			ip_netif->arp = 0;
+		}
+	}
+	// get packet dimensions
+	ERROR_PROPAGATE(nil_packet_size_req(ip_netif->phone, ip_netif->device_id, &ip_netif->packet_dimension));
+	if(ip_netif->packet_dimension.content < IP_MIN_CONTENT){
+		printf("Maximum transmission unit %d bytes is too small, at least %d bytes are needed\n", ip_netif->packet_dimension.content, IP_MIN_CONTENT);
+		ip_netif->packet_dimension.content = IP_MIN_CONTENT;
+	}
+	index = ip_netifs_add(&ip_globals.netifs, ip_netif->device_id, ip_netif);
+	if(index < 0){
+		return index;
+	}
+	if(gateway.s_addr){
+		// the default gateway
+		ip_globals.gateway.address.s_addr = 0;
+		ip_globals.gateway.netmask.s_addr = 0;
+		ip_globals.gateway.gateway.s_addr = gateway.s_addr;
+		ip_globals.gateway.netif = ip_netif;
+	}
+	return EOK;
+}
+
+int ip_mtu_changed_message(device_id_t device_id, size_t mtu){
+	ip_netif_ref netif;
+
+	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
+	netif = ip_netifs_find(&ip_globals.netifs, device_id);
+	if(! netif){
+		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+		return ENOENT;
+	}
+	netif->packet_dimension.content = mtu;
+	printf("%s: Device %d changed MTU to %d\n", NAME, device_id, mtu);
+	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+	return EOK;
+}
+
+int ip_device_state_message(device_id_t device_id, device_state_t state){
+	ip_netif_ref netif;
+
+	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
+	// find the device
+	netif = ip_netifs_find(&ip_globals.netifs, device_id);
+	if(! netif){
+		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+		return ENOENT;
+	}
+	netif->state = state;
+	printf("%s: Device %d changed state to %d\n", NAME, device_id, state);
+	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+	return EOK;
+}
+
+int ip_register(int protocol, services_t service, int phone, tl_received_msg_t received_msg){
+	ip_proto_ref proto;
+	int index;
+
+	if(!(protocol && service && ((phone > 0) || (received_msg)))){
+		return EINVAL;
+	}
+	proto = (ip_proto_ref) malloc(sizeof(ip_protos_t));
+	if(! proto){
+		return ENOMEM;
+	}
+	proto->protocol = protocol;
+	proto->service = service;
+	proto->phone = phone;
+	proto->received_msg = received_msg;
+	fibril_rwlock_write_lock(&ip_globals.protos_lock);
+	index = ip_protos_add(&ip_globals.protos, proto->protocol, proto);
+	if(index < 0){
+		fibril_rwlock_write_unlock(&ip_globals.protos_lock);
+		free(proto);
+		return index;
+	}
+	
+	printf("%s: Protocol registered (protocol: %d, phone: %d)\n",
+	    NAME, proto->protocol, proto->phone);
+	
+	fibril_rwlock_write_unlock(&ip_globals.protos_lock);
+	return EOK;
+}
+
+int ip_send_msg_local(int il_phone, device_id_t device_id, packet_t packet, services_t sender, services_t error){
+	ERROR_DECLARE;
+
 	int addrlen;
 	ip_netif_ref netif;
 	ip_route_ref route;
-	struct sockaddr *addr;
-	struct sockaddr_in *address_in;
-	in_addr_t *dest;
-	in_addr_t *src;
+	struct sockaddr * addr;
+	struct sockaddr_in * address_in;
+//	struct sockaddr_in6 *	address_in6;
+	in_addr_t * dest;
+	in_addr_t * src;
 	int phone;
-	int rc;
 
 	// addresses in the host byte order
 	// should be the next hop address or the target destination address
 	addrlen = packet_get_addr(packet, NULL, (uint8_t **) &addr);
-	if (addrlen < 0)
+	if(addrlen < 0){
 		return ip_release_and_return(packet, addrlen);
-	if ((size_t) addrlen < sizeof(struct sockaddr))
+	}
+	if((size_t) addrlen < sizeof(struct sockaddr)){
 		return ip_release_and_return(packet, EINVAL);
-
-	switch (addr->sa_family) {
-	case AF_INET:
-		if (addrlen != sizeof(struct sockaddr_in))
-			return ip_release_and_return(packet, EINVAL);
-		address_in = (struct sockaddr_in *) addr;
-		dest = &address_in->sin_addr;
-		if (!dest->s_addr)
-			dest->s_addr = IPV4_LOCALHOST_ADDRESS;
-		break;
-	case AF_INET6:
-	default:
-		return ip_release_and_return(packet, EAFNOSUPPORT);
-	}
-
+	}
+	switch(addr->sa_family){
+		case AF_INET:
+			if(addrlen != sizeof(struct sockaddr_in)){
+				return ip_release_and_return(packet, EINVAL);
+			}
+			address_in = (struct sockaddr_in *) addr;
+			dest = &address_in->sin_addr;
+			if(! dest->s_addr){
+				dest->s_addr = IPV4_LOCALHOST_ADDRESS;
+			}
+			break;
+		// TODO IPv6
+/*		case AF_INET6:
+			if(addrlen != sizeof(struct sockaddr_in6)){
+				return EINVAL;
+			}
+			address_in6 = (struct sockaddr_in6 *) dest;
+			address_in6.sin6_addr.s6_addr;
+			IPV6_LOCALHOST_ADDRESS;
+*/		default:
+			return ip_release_and_return(packet, EAFNOSUPPORT);
+	}
 	netif = NULL;
 	route = NULL;
 	fibril_rwlock_read_lock(&ip_globals.netifs_lock);
-
 	// device specified?
-	if (device_id > 0) {
+	if(device_id > 0){
 		netif = ip_netifs_find(&ip_globals.netifs, device_id);
 		route = ip_netif_find_route(netif, * dest);
-		if (netif && !route && (ip_globals.gateway.netif == netif))
+		if(netif && (! route) && (ip_globals.gateway.netif == netif)){
 			route = &ip_globals.gateway;
-	}
-
-	if (!route) {
+		}
+	}
+	if(! route){
 		route = ip_find_route(*dest);
 		netif = route ? route->netif : NULL;
 	}
-	if (!netif || !route) {
+	if(!(netif && route)){
 		fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
 		phone = ip_prepare_icmp_and_get_phone(error, packet, NULL);
-		if (phone >= 0) {
+		if(phone >= 0){
 			// unreachable ICMP if no routing
-			icmp_destination_unreachable_msg(phone,
-			    ICMP_NET_UNREACH, 0, packet);
+			icmp_destination_unreachable_msg(phone, ICMP_NET_UNREACH, 0, packet);
 		}
 		return ENOENT;
 	}
-
-	if (error) {
-		// do not send for broadcast, anycast packets or network
-		// broadcast
-		if (!dest->s_addr || !(~dest->s_addr) ||
-		    !(~((dest->s_addr & ~route->netmask.s_addr) |
-		    route->netmask.s_addr)) ||
-		    (!(dest->s_addr & ~route->netmask.s_addr))) {
+	if(error){
+		// do not send for broadcast, anycast packets or network broadcast
+		if((! dest->s_addr)
+			|| (!(~ dest->s_addr))
+			|| (!(~((dest->s_addr &(~ route->netmask.s_addr)) | route->netmask.s_addr)))
+			|| (!(dest->s_addr &(~ route->netmask.s_addr)))){
 			return ip_release_and_return(packet, EINVAL);
 		}
 	}
-
 	// if the local host is the destination
-	if ((route->address.s_addr == dest->s_addr) &&
-	    (dest->s_addr != IPV4_LOCALHOST_ADDRESS)) {
+	if((route->address.s_addr == dest->s_addr)
+		&& (dest->s_addr != IPV4_LOCALHOST_ADDRESS)){
 		// find the loopback device to deliver
 		dest->s_addr = IPV4_LOCALHOST_ADDRESS;
 		route = ip_find_route(*dest);
 		netif = route ? route->netif : NULL;
-		if (!netif || !route) {
+		if(!(netif && route)){
 			fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
-			phone = ip_prepare_icmp_and_get_phone(error, packet,
-			    NULL);
-			if (phone >= 0) {
+			phone = ip_prepare_icmp_and_get_phone(error, packet, NULL);
+			if(phone >= 0){
 				// unreachable ICMP if no routing
-				icmp_destination_unreachable_msg(phone,
-				    ICMP_HOST_UNREACH, 0, packet);
+				icmp_destination_unreachable_msg(phone, ICMP_HOST_UNREACH, 0, packet);
 			}
 			return ENOENT;
 		}
 	}
-	
 	src = ip_netif_address(netif);
-	if (!src) {
+	if(! src){
 		fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
 		return ip_release_and_return(packet, ENOENT);
 	}
-
-	rc = ip_send_route(packet, netif, route, src, *dest, error);
+	ERROR_CODE = ip_send_route(packet, netif, route, src, * dest, error);
 	fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
-
-	return rc;
-}
-
-/** Returns the device packet dimensions for sending.
- *
- * @param[in] phone	The service module phone.
- * @param[in] message	The service specific message.
- * @param[in] device_id	The device identifier.
- * @param[out] addr_len	The minimum reserved address length.
- * @param[out] prefix	The minimum reserved prefix size.
- * @param[out] content	The maximum content size.
- * @param[out] suffix	The minimum reserved suffix size.
- * @returns		EOK on success.
- */
-static int
-ip_packet_size_message(device_id_t device_id, size_t *addr_len, size_t *prefix,
-    size_t *content, size_t *suffix)
+	return ERROR_CODE;
+}
+
+in_addr_t * ip_netif_address(ip_netif_ref netif){
+	ip_route_ref route;
+
+	route = ip_routes_get_index(&netif->routes, 0);
+	return route ? &route->address : NULL;
+}
+
+int ip_send_route(packet_t packet, ip_netif_ref netif, ip_route_ref route, in_addr_t * src, in_addr_t dest, services_t error){
+	ERROR_DECLARE;
+
+	measured_string_t destination;
+	measured_string_ref translation;
+	char * data;
+	int phone;
+
+	// get destination hardware address
+	if(netif->arp && (route->address.s_addr != dest.s_addr)){
+		destination.value = route->gateway.s_addr ? (char *) &route->gateway.s_addr : (char *) &dest.s_addr;
+		destination.length = CONVERT_SIZE(dest.s_addr, char, 1);
+		if(ERROR_OCCURRED(arp_translate_req(netif->arp->phone, netif->device_id, SERVICE_IP, &destination, &translation, &data))){
+//			sleep(1);
+//			ERROR_PROPAGATE(arp_translate_req(netif->arp->phone, netif->device_id, SERVICE_IP, &destination, &translation, &data));
+			pq_release_remote(ip_globals.net_phone, packet_get_id(packet));
+			return ERROR_CODE;
+		}
+		if(!(translation && translation->value)){
+			if(translation){
+				free(translation);
+				free(data);
+			}
+			phone = ip_prepare_icmp_and_get_phone(error, packet, NULL);
+			if(phone >= 0){
+				// unreachable ICMP if no routing
+				icmp_destination_unreachable_msg(phone, ICMP_HOST_UNREACH, 0, packet);
+			}
+			return EINVAL;
+		}
+	}else translation = NULL;
+	if(ERROR_OCCURRED(ip_prepare_packet(src, dest, packet, translation))){
+		pq_release_remote(ip_globals.net_phone, packet_get_id(packet));
+	}else{
+		packet = ip_split_packet(packet, netif->packet_dimension.prefix, netif->packet_dimension.content, netif->packet_dimension.suffix, netif->packet_dimension.addr_len, error);
+		if(packet){
+			nil_send_msg(netif->phone, netif->device_id, packet, SERVICE_IP);
+		}
+	}
+	if(translation){
+		free(translation);
+		free(data);
+	}
+	return ERROR_CODE;
+}
+
+int ip_prepare_packet(in_addr_t * source, in_addr_t dest, packet_t packet, measured_string_ref destination){
+	ERROR_DECLARE;
+
+	size_t length;
+	ip_header_ref header;
+	ip_header_ref last_header;
+	ip_header_ref middle_header;
+	packet_t next;
+
+	length = packet_get_data_length(packet);
+	if((length < sizeof(ip_header_t)) || (length > IP_MAX_CONTENT)){
+		return EINVAL;
+	}
+	header = (ip_header_ref) packet_get_data(packet);
+	if(destination){
+		ERROR_PROPAGATE(packet_set_addr(packet, NULL, (uint8_t *) destination->value, CONVERT_SIZE(char, uint8_t, destination->length)));
+	}else{
+		ERROR_PROPAGATE(packet_set_addr(packet, NULL, NULL, 0));
+	}
+	header->version = IPV4;
+	header->fragment_offset_high = 0;
+	header->fragment_offset_low = 0;
+	header->header_checksum = 0;
+	if(source){
+		header->source_address = source->s_addr;
+	}
+	header->destination_address = dest.s_addr;
+	fibril_rwlock_write_lock(&ip_globals.lock);
+	++ ip_globals.packet_counter;
+	header->identification = htons(ip_globals.packet_counter);
+	fibril_rwlock_write_unlock(&ip_globals.lock);
+//	length = packet_get_data_length(packet);
+	if(pq_next(packet)){
+		last_header = (ip_header_ref) malloc(IP_HEADER_LENGTH(header));
+		if(! last_header){
+			return ENOMEM;
+		}
+		ip_create_last_header(last_header, header);
+		next = pq_next(packet);
+		while(pq_next(next)){
+			middle_header = (ip_header_ref) packet_prefix(next, IP_HEADER_LENGTH(last_header));
+			if(! middle_header){
+				return ENOMEM;
+			}
+			memcpy(middle_header, last_header, IP_HEADER_LENGTH(last_header));
+			header->flags |= IPFLAG_MORE_FRAGMENTS;
+			middle_header->total_length = htons(packet_get_data_length(next));
+			middle_header->fragment_offset_high = IP_COMPUTE_FRAGMENT_OFFSET_HIGH(length);
+			middle_header->fragment_offset_low = IP_COMPUTE_FRAGMENT_OFFSET_LOW(length);
+			middle_header->header_checksum = IP_HEADER_CHECKSUM(middle_header);
+			if(destination){
+				ERROR_PROPAGATE(packet_set_addr(next, NULL, (uint8_t *) destination->value, CONVERT_SIZE(char, uint8_t, destination->length)));
+			}
+			length += packet_get_data_length(next);
+			next = pq_next(next);
+		}
+		middle_header = (ip_header_ref) packet_prefix(next, IP_HEADER_LENGTH(last_header));
+		if(! middle_header){
+			return ENOMEM;
+		}
+		memcpy(middle_header, last_header, IP_HEADER_LENGTH(last_header));
+		middle_header->total_length = htons(packet_get_data_length(next));
+		middle_header->fragment_offset_high = IP_COMPUTE_FRAGMENT_OFFSET_HIGH(length);
+		middle_header->fragment_offset_low = IP_COMPUTE_FRAGMENT_OFFSET_LOW(length);
+		middle_header->header_checksum = IP_HEADER_CHECKSUM(middle_header);
+		if(destination){
+			ERROR_PROPAGATE(packet_set_addr(next, NULL, (uint8_t *) destination->value, CONVERT_SIZE(char, uint8_t, destination->length)));
+		}
+		length += packet_get_data_length(next);
+		free(last_header);
+		header->flags |= IPFLAG_MORE_FRAGMENTS;
+	}
+	header->total_length = htons(length);
+	// unnecessary for all protocols
+	header->header_checksum = IP_HEADER_CHECKSUM(header);
+	return EOK;
+}
+
+int ip_message_standalone(ipc_callid_t callid, ipc_call_t *call,
+    ipc_call_t *answer, int * answer_count)
 {
-	ip_netif_ref netif;
-	int index;
-
-	if (!addr_len || !prefix || !content || !suffix)
-		return EBADMEM;
-
-	*content = IP_MAX_CONTENT - IP_PREFIX;
-	fibril_rwlock_read_lock(&ip_globals.netifs_lock);
-	if (device_id < 0) {
-		*addr_len = IP_ADDR;
-		*prefix = 0;
-		*suffix = 0;
-
-		for (index = ip_netifs_count(&ip_globals.netifs) - 1;
-		    index >= 0; index--) {
-			netif = ip_netifs_get_index(&ip_globals.netifs, index);
-			if (!netif)
-				continue;
-			
-			if (netif->packet_dimension.addr_len > *addr_len)
-				*addr_len = netif->packet_dimension.addr_len;
-			
-			if (netif->packet_dimension.prefix > *prefix)
-				*prefix = netif->packet_dimension.prefix;
-				
-			if (netif->packet_dimension.suffix > *suffix)
-				*suffix = netif->packet_dimension.suffix;
-		}
-
-		*prefix = *prefix + IP_PREFIX;
-		*suffix = *suffix + IP_SUFFIX;
-	} else {
-		netif = ip_netifs_find(&ip_globals.netifs, device_id);
-		if (!netif) {
-			fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
-			return ENOENT;
-		}
-
-		*addr_len = (netif->packet_dimension.addr_len > IP_ADDR) ?
-		    netif->packet_dimension.addr_len : IP_ADDR;
-		*prefix = netif->packet_dimension.prefix + IP_PREFIX;
-		*suffix = netif->packet_dimension.suffix + IP_SUFFIX;
-	}
-	fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
-
-	return EOK;
-}
-
-/** Returns the packet destination address from the IP header.
- *
- * @param[in] header	The packet IP header to be read.
- * @returns		The packet destination address.
- */
-static in_addr_t ip_get_destination(ip_header_ref header)
-{
-	in_addr_t destination;
-
-	// TODO search set ipopt route?
-	destination.s_addr = header->destination_address;
-	return destination;
-}
-
-/** Delivers the packet to the local host.
- *
- * The packet is either passed to another module or released on error.
- * The ICMP_PROT_UNREACH error notification may be sent if the protocol is not
- * found.
- *
- * @param[in] device_id	The source device identifier.
- * @param[in] packet	The packet to be delivered.
- * @param[in] header	The first packet IP header. May be NULL.
- * @param[in] error	The packet error service.
- * @returns		EOK on success.
- * @returns		ENOTSUP if the packet is a fragment.
- * @returns		EAFNOSUPPORT if the address family is not supported.
- * @returns		ENOENT if the target protocol is not found.
- * @returns		Other error codes as defined for the packet_set_addr()
- *			function.
- * @returns		Other error codes as defined for the packet_trim()
- *			function.
- * @returns		Other error codes as defined for the protocol specific
- *			tl_received_msg() function.
- */
-static int
-ip_deliver_local(device_id_t device_id, packet_t packet, ip_header_ref header,
-    services_t error)
-{
-	ip_proto_ref proto;
-	int phone;
-	services_t service;
-	tl_received_msg_t received_msg;
-	struct sockaddr *src;
-	struct sockaddr *dest;
-	struct sockaddr_in src_in;
-	struct sockaddr_in dest_in;
-	socklen_t addrlen;
-	int rc;
-
-	if ((header->flags & IPFLAG_MORE_FRAGMENTS) ||
-	    IP_FRAGMENT_OFFSET(header)) {
-		// TODO fragmented
-		return ENOTSUP;
-	}
+	ERROR_DECLARE;
 	
-	switch (header->version) {
-	case IPVERSION:
-		addrlen = sizeof(src_in);
-		bzero(&src_in, addrlen);
-		src_in.sin_family = AF_INET;
-		memcpy(&dest_in, &src_in, addrlen);
-		memcpy(&src_in.sin_addr.s_addr, &header->source_address,
-		    sizeof(header->source_address));
-		memcpy(&dest_in.sin_addr.s_addr, &header->destination_address,
-		    sizeof(header->destination_address));
-		src = (struct sockaddr *) &src_in;
-		dest = (struct sockaddr *) &dest_in;
-		break;
-
-	default:
-		return ip_release_and_return(packet, EAFNOSUPPORT);
-	}
-
-	rc = packet_set_addr(packet, (uint8_t *) src, (uint8_t *) dest,
-	    addrlen);
-	if (rc != EOK)
-		return ip_release_and_return(packet, rc);
-
-	// trim padding if present
-	if (!error &&
-	    (IP_TOTAL_LENGTH(header) < packet_get_data_length(packet))) {
-		rc = packet_trim(packet, 0,
-		    packet_get_data_length(packet) - IP_TOTAL_LENGTH(header));
-		if (rc != EOK)
-			return ip_release_and_return(packet, rc);
-	}
-
-	fibril_rwlock_read_lock(&ip_globals.protos_lock);
-
-	proto = ip_protos_find(&ip_globals.protos, header->protocol);
-	if (!proto) {
-		fibril_rwlock_read_unlock(&ip_globals.protos_lock);
-		phone = ip_prepare_icmp_and_get_phone(error, packet, header);
-		if (phone >= 0) {
-			// unreachable ICMP
-			icmp_destination_unreachable_msg(phone,
-			    ICMP_PROT_UNREACH, 0, packet);
-		}
-		return ENOENT;
-	}
-
-	if (proto->received_msg) {
-		service = proto->service;
-		received_msg = proto->received_msg;
-		fibril_rwlock_read_unlock(&ip_globals.protos_lock);
-		rc = received_msg(device_id, packet, service, error);
-	} else {
-		rc = tl_received_msg(proto->phone, device_id, packet,
-		    proto->service, error);
-		fibril_rwlock_read_unlock(&ip_globals.protos_lock);
-	}
-
-	return rc;
-}
-
-/** Processes the received packet.
- *
- * The packet is either passed to another module or released on error.
- *
- * The ICMP_PARAM_POINTER error notification may be sent if the checksum is
- * invalid.
- * The ICMP_EXC_TTL error notification may be sent if the TTL is less than two.
- * The ICMP_HOST_UNREACH error notification may be sent if no route was found.
- * The ICMP_HOST_UNREACH error notification may be sent if the packet is for
- * another host and the routing is disabled.
- *
- * @param[in] device_id	The source device identifier.
- * @param[in] packet	The received packet to be processed.
- * @returns		EOK on success.
- * @returns		EINVAL if the TTL is less than two.
- * @returns		EINVAL if the checksum is invalid.
- * @returns		EAFNOSUPPORT if the address family is not supported.
- * @returns		ENOENT if no route was found.
- * @returns		ENOENT if the packet is for another host and the routing
- *			is disabled.
- */
-static int
-ip_process_packet(device_id_t device_id, packet_t packet)
-{
-	ip_header_ref header;
-	in_addr_t dest;
-	ip_route_ref route;
-	int phone;
-	struct sockaddr *addr;
-	struct sockaddr_in addr_in;
-	socklen_t addrlen;
-	int rc;
-
-	header = (ip_header_ref) packet_get_data(packet);
-	if (!header)
-		return ip_release_and_return(packet, ENOMEM);
-
-	// checksum
-	if ((header->header_checksum) &&
-	    (IP_HEADER_CHECKSUM(header) != IP_CHECKSUM_ZERO)) {
-		phone = ip_prepare_icmp_and_get_phone(0, packet, header);
-		if (phone >= 0) {
-			// checksum error ICMP
-			icmp_parameter_problem_msg(phone, ICMP_PARAM_POINTER,
-			    ((size_t) ((void *) &header->header_checksum)) -
-			    ((size_t) ((void *) header)), packet);
-		}
-		return EINVAL;
-	}
-
-	if (header->ttl <= 1) {
-		phone = ip_prepare_icmp_and_get_phone(0, packet, header);
-		if (phone >= 0) {
-			// ttl exceeded ICMP
-			icmp_time_exceeded_msg(phone, ICMP_EXC_TTL, packet);
-		}
-		return EINVAL;
-	}
-
-	// process ipopt and get destination
-	dest = ip_get_destination(header);
-
-	// set the addrination address
-	switch (header->version) {
-	case IPVERSION:
-		addrlen = sizeof(addr_in);
-		bzero(&addr_in, addrlen);
-		addr_in.sin_family = AF_INET;
-		memcpy(&addr_in.sin_addr.s_addr, &dest, sizeof(dest));
-		addr = (struct sockaddr *) &addr_in;
-		break;
-
-	default:
-		return ip_release_and_return(packet, EAFNOSUPPORT);
-	}
-
-	rc = packet_set_addr(packet, NULL, (uint8_t *) &addr, addrlen);
-	if (rc != EOK)
-		return rc;
-
-	route = ip_find_route(dest);
-	if (!route) {
-		phone = ip_prepare_icmp_and_get_phone(0, packet, header);
-		if (phone >= 0) {
-			// unreachable ICMP
-			icmp_destination_unreachable_msg(phone,
-			    ICMP_HOST_UNREACH, 0, packet);
-		}
-		return ENOENT;
-	}
-
-	if (route->address.s_addr == dest.s_addr) {
-		// local delivery
-		return ip_deliver_local(device_id, packet, header, 0);
-	}
-
-	if (route->netif->routing) {
-		header->ttl--;
-		return ip_send_route(packet, route->netif, route, NULL, dest,
-		    0);
-	}
-
-	phone = ip_prepare_icmp_and_get_phone(0, packet, header);
-	if (phone >= 0) {
-		// unreachable ICMP if no routing
-		icmp_destination_unreachable_msg(phone, ICMP_HOST_UNREACH, 0,
-		    packet);
-	}
-	
-	return ENOENT;
-}
-
-static int
-ip_add_route_req_local(int ip_phone, device_id_t device_id, in_addr_t address,
-    in_addr_t netmask, in_addr_t gateway)
-{
-	ip_route_ref route;
-	ip_netif_ref netif;
-	int index;
-
-	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
-
-	netif = ip_netifs_find(&ip_globals.netifs, device_id);
-	if (!netif) {
-		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-		return ENOENT;
-	}
-
-	route = (ip_route_ref) malloc(sizeof(ip_route_t));
-	if (!route) {
-		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-		return ENOMEM;
-	}
-
-	route->address.s_addr = address.s_addr;
-	route->netmask.s_addr = netmask.s_addr;
-	route->gateway.s_addr = gateway.s_addr;
-	route->netif = netif;
-	index = ip_routes_add(&netif->routes, route);
-	if (index < 0)
-		free(route);
-
-	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-	
-	return index;
-}
-
-static int
-ip_set_gateway_req_local(int ip_phone, device_id_t device_id, in_addr_t gateway)
-{
-	ip_netif_ref netif;
-
-	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
-
-	netif = ip_netifs_find(&ip_globals.netifs, device_id);
-	if (!netif) {
-		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-		return ENOENT;
-	}
-
-	ip_globals.gateway.address.s_addr = 0;
-	ip_globals.gateway.netmask.s_addr = 0;
-	ip_globals.gateway.gateway.s_addr = gateway.s_addr;
-	ip_globals.gateway.netif = netif;
-	
-	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
-	
-	return EOK;
-}
-
-/** Notify the IP module about the received error notification packet.
- *
- * @param[in] ip_phone	The IP module phone used for (semi)remote calls.
- * @param[in] device_id	The device identifier.
- * @param[in] packet	The received packet or the received packet queue.
- * @param[in] target	The target internetwork module service to be
- *			delivered to.
- * @param[in] error	The packet error reporting service. Prefixes the
- *			received packet.
- * @return		EOK on success.
- *
- */
-static int
-ip_received_error_msg_local(int ip_phone, device_id_t device_id,
-    packet_t packet, services_t target, services_t error)
-{
-	uint8_t *data;
-	int offset;
-	icmp_type_t type;
-	icmp_code_t code;
-	ip_netif_ref netif;
-	measured_string_t address;
-	ip_route_ref route;
-	ip_header_ref header;
-
-	switch (error) {
-	case SERVICE_ICMP:
-		offset = icmp_client_process_packet(packet, &type, &code, NULL,
-		    NULL);
-		if (offset < 0)
-			return ip_release_and_return(packet, ENOMEM);
-
-		data = packet_get_data(packet);
-		header = (ip_header_ref)(data + offset);
-
-		// destination host unreachable?
-		if ((type != ICMP_DEST_UNREACH) ||
-		    (code != ICMP_HOST_UNREACH)) {
-		    	// no, something else
-			break;
-		}
-
-		fibril_rwlock_read_lock(&ip_globals.netifs_lock);
-
-		netif = ip_netifs_find(&ip_globals.netifs, device_id);
-		if (!netif || !netif->arp) {
-			fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
-			break;
-		}
-
-		route = ip_routes_get_index(&netif->routes, 0);
-
-		// from the same network?
-		if (route && ((route->address.s_addr & route->netmask.s_addr) ==
-		    (header->destination_address & route->netmask.s_addr))) {
-			// clear the ARP mapping if any
-			address.value = (char *) &header->destination_address;
-			address.length = CONVERT_SIZE(uint8_t, char,
-			    sizeof(header->destination_address));
-			arp_clear_address_req(netif->arp->phone,
-			    netif->device_id, SERVICE_IP, &address);
-		}
-
-		fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
-		break;
-
-	default:
-		return ip_release_and_return(packet, ENOTSUP);
-	}
-
-	return ip_deliver_local(device_id, packet, header, error);
-}
-
-static int
-ip_get_route_req_local(int ip_phone, ip_protocol_t protocol,
-    const struct sockaddr *destination, socklen_t addrlen,
-    device_id_t *device_id, void **header, size_t *headerlen)
-{
-	struct sockaddr_in *address_in;
-	in_addr_t *dest;
-	in_addr_t *src;
-	ip_route_ref route;
-	ipv4_pseudo_header_ref header_in;
-
-	if (!destination || (addrlen <= 0))
-		return EINVAL;
-
-	if (!device_id || !header || !headerlen)
-		return EBADMEM;
-
-	if ((size_t) addrlen < sizeof(struct sockaddr))
-		return EINVAL;
-
-	switch (destination->sa_family) {
-	case AF_INET:
-		if (addrlen != sizeof(struct sockaddr_in))
-			return EINVAL;
-		address_in = (struct sockaddr_in *) destination;
-		dest = &address_in->sin_addr;
-		if (!dest->s_addr)
-			dest->s_addr = IPV4_LOCALHOST_ADDRESS;
-		break;
-
-	case AF_INET6:
-	default:
-		return EAFNOSUPPORT;
-	}
-
-	fibril_rwlock_read_lock(&ip_globals.lock);
-	route = ip_find_route(*dest);
-	// if the local host is the destination
-	if (route && (route->address.s_addr == dest->s_addr) &&
-	    (dest->s_addr != IPV4_LOCALHOST_ADDRESS)) {
-		// find the loopback device to deliver
-		dest->s_addr = IPV4_LOCALHOST_ADDRESS;
-		route = ip_find_route(*dest);
-	}
-
-	if (!route || !route->netif) {
-		fibril_rwlock_read_unlock(&ip_globals.lock);
-		return ENOENT;
-	}
-
-	*device_id = route->netif->device_id;
-	src = ip_netif_address(route->netif);
-	fibril_rwlock_read_unlock(&ip_globals.lock);
-
-	*headerlen = sizeof(*header_in);
-	header_in = (ipv4_pseudo_header_ref) malloc(*headerlen);
-	if (!header_in)
-		return ENOMEM;
-
-	bzero(header_in, *headerlen);
-	header_in->destination_address = dest->s_addr;
-	header_in->source_address = src->s_addr;
-	header_in->protocol = protocol;
-	header_in->data_length = 0;
-	*header = header_in;
-
-	return EOK;
-}
-
-/** Processes the received IP packet or the packet queue one by one.
- *
- * The packet is either passed to another module or released on error.
- *
- * @param[in] device_id	The source device identifier.
- * @param[in,out] packet The received packet.
- * @returns		EOK on success and the packet is no longer needed.
- * @returns		EINVAL if the packet is too small to carry the IP
- *			packet.
- * @returns		EINVAL if the received address lengths differs from the
- *			registered values.
- * @returns		ENOENT if the device is not found in the cache.
- * @returns		ENOENT if the protocol for the device is not found in
- *			the cache.
- * @returns		ENOMEM if there is not enough memory left.
- */
-static int ip_receive_message(device_id_t device_id, packet_t packet)
-{
-	packet_t next;
-
-	do {
-		next = pq_detach(packet);
-		ip_process_packet(device_id, packet);
-		packet = next;
-	} while (packet);
-
-	return EOK;
-}
-
-/** Processes the IP message.
- *
- * @param[in] callid	The message identifier.
- * @param[in] call	The message parameters.
- * @param[out] answer	The message answer parameters.
- * @param[out] answer_count The last parameter for the actual answer in the
- *			answer parameter.
- * @returns		EOK on success.
- * @returns		ENOTSUP if the message is not known.
- *
- * @see ip_interface.h
- * @see il_interface.h
- * @see IS_NET_IP_MESSAGE()
- */
-int
-ip_message_standalone(ipc_callid_t callid, ipc_call_t *call, ipc_call_t *answer,
-    int *answer_count)
-{
 	packet_t packet;
 	struct sockaddr *addr;
@@ -1899,93 +901,64 @@
 	size_t headerlen;
 	device_id_t device_id;
-	int rc;
 	
 	*answer_count = 0;
 	switch (IPC_GET_METHOD(*call)) {
-	case IPC_M_PHONE_HUNGUP:
-		return EOK;
-	
-	case IPC_M_CONNECT_TO_ME:
-		return ip_register(IL_GET_PROTO(call), IL_GET_SERVICE(call),
-		    IPC_GET_PHONE(call), NULL);
-	
-	case NET_IL_DEVICE:
-		return ip_device_req_local(0, IPC_GET_DEVICE(call),
-		    IPC_GET_SERVICE(call));
-	
-	case NET_IL_SEND:
-		rc = packet_translate_remote(ip_globals.net_phone, &packet,
-		    IPC_GET_PACKET(call));
-		if (rc != EOK)
-			return rc;
-		return ip_send_msg_local(0, IPC_GET_DEVICE(call), packet, 0,
-		    IPC_GET_ERROR(call));
-	
-	case NET_IL_DEVICE_STATE:
-		return ip_device_state_message(IPC_GET_DEVICE(call),
-		    IPC_GET_STATE(call));
-	
-	case NET_IL_RECEIVED:
-		rc = packet_translate_remote(ip_globals.net_phone, &packet,
-		    IPC_GET_PACKET(call));
-		if (rc != EOK)
-			return rc;
-		return ip_receive_message(IPC_GET_DEVICE(call), packet);
-	
-	case NET_IP_RECEIVED_ERROR:
-		rc = packet_translate_remote(ip_globals.net_phone, &packet,
-		    IPC_GET_PACKET(call));
-		if (rc != EOK)
-			return rc;
-		return ip_received_error_msg_local(0, IPC_GET_DEVICE(call),
-		    packet, IPC_GET_TARGET(call), IPC_GET_ERROR(call));
-	
-	case NET_IP_ADD_ROUTE:
-		return ip_add_route_req_local(0, IPC_GET_DEVICE(call),
-		    IP_GET_ADDRESS(call), IP_GET_NETMASK(call),
-		    IP_GET_GATEWAY(call));
-
-	case NET_IP_SET_GATEWAY:
-		return ip_set_gateway_req_local(0, IPC_GET_DEVICE(call),
-		    IP_GET_GATEWAY(call));
-
-	case NET_IP_GET_ROUTE:
-		rc = data_receive((void **) &addr, &addrlen);
-		if (rc != EOK)
-			return rc;
-		
-		rc = ip_get_route_req_local(0, IP_GET_PROTOCOL(call), addr,
-		    (socklen_t) addrlen, &device_id, &header, &headerlen);
-		if (rc != EOK)
-			return rc;
-		
-		IPC_SET_DEVICE(answer, device_id);
-		IP_SET_HEADERLEN(answer, headerlen);
-		
-		*answer_count = 2;
-		
-		rc = data_reply(&headerlen, sizeof(headerlen));
-		if (rc == EOK)
-			rc = data_reply(header, headerlen);
+		case IPC_M_PHONE_HUNGUP:
+			return EOK;
+		case NET_IL_DEVICE:
+			return ip_device_req_local(0, IPC_GET_DEVICE(call),
+			    IPC_GET_SERVICE(call));
+		case IPC_M_CONNECT_TO_ME:
+			return ip_register(IL_GET_PROTO(call), IL_GET_SERVICE(call),
+			    IPC_GET_PHONE(call), NULL);
+		case NET_IL_SEND:
+			ERROR_PROPAGATE(packet_translate_remote(ip_globals.net_phone, &packet,
+			    IPC_GET_PACKET(call)));
+			return ip_send_msg_local(0, IPC_GET_DEVICE(call), packet, 0,
+			    IPC_GET_ERROR(call));
+		case NET_IL_DEVICE_STATE:
+			return ip_device_state_message(IPC_GET_DEVICE(call),
+			    IPC_GET_STATE(call));
+		case NET_IL_RECEIVED:
+			ERROR_PROPAGATE(packet_translate_remote(ip_globals.net_phone, &packet,
+			    IPC_GET_PACKET(call)));
+			return ip_receive_message(IPC_GET_DEVICE(call), packet);
+		case NET_IP_RECEIVED_ERROR:
+			ERROR_PROPAGATE(packet_translate_remote(ip_globals.net_phone, &packet,
+			    IPC_GET_PACKET(call)));
+			return ip_received_error_msg_local(0, IPC_GET_DEVICE(call), packet,
+			    IPC_GET_TARGET(call), IPC_GET_ERROR(call));
+		case NET_IP_ADD_ROUTE:
+			return ip_add_route_req_local(0, IPC_GET_DEVICE(call),
+			    IP_GET_ADDRESS(call), IP_GET_NETMASK(call), IP_GET_GATEWAY(call));
+		case NET_IP_SET_GATEWAY:
+			return ip_set_gateway_req_local(0, IPC_GET_DEVICE(call),
+			    IP_GET_GATEWAY(call));
+		case NET_IP_GET_ROUTE:
+			ERROR_PROPAGATE(data_receive((void **) &addr, &addrlen));
+			ERROR_PROPAGATE(ip_get_route_req_local(0, IP_GET_PROTOCOL(call),
+			    addr, (socklen_t) addrlen, &device_id, &header, &headerlen));
+			IPC_SET_DEVICE(answer, device_id);
+			IP_SET_HEADERLEN(answer, headerlen);
 			
-		free(header);
-		return rc;
-	
-	case NET_IL_PACKET_SPACE:
-		rc = ip_packet_size_message(IPC_GET_DEVICE(call), &addrlen,
-		    &prefix, &content, &suffix);
-		if (rc != EOK)
-			return rc;
-		
-		IPC_SET_ADDR(answer, addrlen);
-		IPC_SET_PREFIX(answer, prefix);
-		IPC_SET_CONTENT(answer, content);
-		IPC_SET_SUFFIX(answer, suffix);
-		*answer_count = 4;
-		return EOK;
-	
-	case NET_IL_MTU_CHANGED:
-		return ip_mtu_changed_message(IPC_GET_DEVICE(call),
-		    IPC_GET_MTU(call));
+			*answer_count = 2;
+			
+			if (!ERROR_OCCURRED(data_reply(&headerlen, sizeof(headerlen))))
+				ERROR_CODE = data_reply(header, headerlen);
+			
+			free(header);
+			return ERROR_CODE;
+		case NET_IL_PACKET_SPACE:
+			ERROR_PROPAGATE(ip_packet_size_message(IPC_GET_DEVICE(call),
+			    &addrlen, &prefix, &content, &suffix));
+			IPC_SET_ADDR(answer, addrlen);
+			IPC_SET_PREFIX(answer, prefix);
+			IPC_SET_CONTENT(answer, content);
+			IPC_SET_SUFFIX(answer, suffix);
+			*answer_count = 4;
+			return EOK;
+		case NET_IL_MTU_CHANGED:
+			return ip_mtu_changed_message(IPC_GET_DEVICE(call),
+			    IPC_GET_MTU(call));
 	}
 	
@@ -1993,10 +966,704 @@
 }
 
+int ip_packet_size_req_local(int ip_phone, device_id_t device_id,
+    packet_dimension_ref packet_dimension)
+{
+	if (!packet_dimension)
+		return EBADMEM;
+	
+	return ip_packet_size_message(device_id, &packet_dimension->addr_len,
+	    &packet_dimension->prefix, &packet_dimension->content,
+	    &packet_dimension->suffix);
+}
+
+int ip_packet_size_message(device_id_t device_id, size_t * addr_len, size_t * prefix, size_t * content, size_t * suffix){
+	ip_netif_ref netif;
+	int index;
+
+	if(!(addr_len && prefix && content && suffix)){
+		return EBADMEM;
+	}
+	*content = IP_MAX_CONTENT - IP_PREFIX;
+	fibril_rwlock_read_lock(&ip_globals.netifs_lock);
+	if(device_id < 0){
+		*addr_len = IP_ADDR;
+		*prefix = 0;
+		*suffix = 0;
+		for(index = ip_netifs_count(&ip_globals.netifs) - 1; index >= 0; -- index){
+			netif = ip_netifs_get_index(&ip_globals.netifs, index);
+			if(netif){
+				if(netif->packet_dimension.addr_len > * addr_len){
+					*addr_len = netif->packet_dimension.addr_len;
+				}
+				if(netif->packet_dimension.prefix > * prefix){
+					*prefix = netif->packet_dimension.prefix;
+				}
+				if(netif->packet_dimension.suffix > * suffix){
+					*suffix = netif->packet_dimension.suffix;
+				}
+			}
+		}
+		*prefix = * prefix + IP_PREFIX;
+		*suffix = * suffix + IP_SUFFIX;
+	}else{
+		netif = ip_netifs_find(&ip_globals.netifs, device_id);
+		if(! netif){
+			fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
+			return ENOENT;
+		}
+		*addr_len = (netif->packet_dimension.addr_len > IP_ADDR) ? netif->packet_dimension.addr_len : IP_ADDR;
+		*prefix = netif->packet_dimension.prefix + IP_PREFIX;
+		*suffix = netif->packet_dimension.suffix + IP_SUFFIX;
+	}
+	fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
+	return EOK;
+}
+
+int ip_add_route_req_local(int ip_phone, device_id_t device_id, in_addr_t address, in_addr_t netmask, in_addr_t gateway){
+	ip_route_ref route;
+	ip_netif_ref netif;
+	int index;
+
+	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
+	netif = ip_netifs_find(&ip_globals.netifs, device_id);
+	if(! netif){
+		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+		return ENOENT;
+	}
+	route = (ip_route_ref) malloc(sizeof(ip_route_t));
+	if(! route){
+		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+		return ENOMEM;
+	}
+	route->address.s_addr = address.s_addr;
+	route->netmask.s_addr = netmask.s_addr;
+	route->gateway.s_addr = gateway.s_addr;
+	route->netif = netif;
+	index = ip_routes_add(&netif->routes, route);
+	if(index < 0){
+		free(route);
+	}
+	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+	return index;
+}
+
+ip_route_ref ip_find_route(in_addr_t destination){
+	int index;
+	ip_route_ref route;
+	ip_netif_ref netif;
+
+	// start with the last netif - the newest one
+	index = ip_netifs_count(&ip_globals.netifs) - 1;
+	while(index >= 0){
+		netif = ip_netifs_get_index(&ip_globals.netifs, index);
+		if(netif && (netif->state == NETIF_ACTIVE)){
+			route = ip_netif_find_route(netif, destination);
+			if(route){
+				return route;
+			}
+		}
+		-- index;
+	}
+	return &ip_globals.gateway;
+}
+
+ip_route_ref ip_netif_find_route(ip_netif_ref netif, in_addr_t destination){
+	int index;
+	ip_route_ref route;
+
+	if(netif){
+		// start with the first one - the direct route
+		for(index = 0; index < ip_routes_count(&netif->routes); ++ index){
+			route = ip_routes_get_index(&netif->routes, index);
+			if(route && ((route->address.s_addr &route->netmask.s_addr) == (destination.s_addr &route->netmask.s_addr))){
+				return route;
+			}
+		}
+	}
+	return NULL;
+}
+
+int ip_set_gateway_req_local(int ip_phone, device_id_t device_id, in_addr_t gateway)
+{
+	ip_netif_ref netif;
+
+	fibril_rwlock_write_lock(&ip_globals.netifs_lock);
+	netif = ip_netifs_find(&ip_globals.netifs, device_id);
+	if(! netif){
+		fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+		return ENOENT;
+	}
+	ip_globals.gateway.address.s_addr = 0;
+	ip_globals.gateway.netmask.s_addr = 0;
+	ip_globals.gateway.gateway.s_addr = gateway.s_addr;
+	ip_globals.gateway.netif = netif;
+	fibril_rwlock_write_unlock(&ip_globals.netifs_lock);
+	return EOK;
+}
+
+packet_t ip_split_packet(packet_t packet, size_t prefix, size_t content, size_t suffix, socklen_t addr_len, services_t error){
+	size_t length;
+	packet_t next;
+	packet_t new_packet;
+	int result;
+	int phone;
+
+	next = packet;
+	// check all packets
+	while(next){
+		length = packet_get_data_length(next);
+		// too long?
+		if(length > content){
+			result = ip_fragment_packet(next, content, prefix, suffix, addr_len);
+			if(result != EOK){
+				new_packet = pq_detach(next);
+				if(next == packet){
+					// the new first packet of the queue
+					packet = new_packet;
+				}
+				// fragmentation needed?
+				if(result == EPERM){
+					phone = ip_prepare_icmp_and_get_phone(error, next, NULL);
+					if(phone >= 0){
+						// fragmentation necessary ICMP
+						icmp_destination_unreachable_msg(phone, ICMP_FRAG_NEEDED, content, next);
+					}
+				}else{
+					pq_release_remote(ip_globals.net_phone, packet_get_id(next));
+				}
+				next = new_packet;
+				continue;
+			}
+		}
+		next = pq_next(next);
+	}
+	return packet;
+}
+
+int ip_fragment_packet(packet_t packet, size_t length, size_t prefix, size_t suffix, socklen_t addr_len){
+	ERROR_DECLARE;
+
+	packet_t new_packet;
+	ip_header_ref header;
+	ip_header_ref middle_header;
+	ip_header_ref last_header;
+	struct sockaddr * src;
+	struct sockaddr * dest;
+	socklen_t addrlen;
+	int result;
+
+	result = packet_get_addr(packet, (uint8_t **) &src, (uint8_t **) &dest);
+	if(result <= 0){
+		return EINVAL;
+	}
+	addrlen = (socklen_t) result;
+	if(packet_get_data_length(packet) <= sizeof(ip_header_t)){
+		return ENOMEM;
+	}
+	// get header
+	header = (ip_header_ref) packet_get_data(packet);
+	if(! header){
+		return EINVAL;
+	}
+	// fragmentation forbidden?
+	if(header->flags &IPFLAG_DONT_FRAGMENT){
+		return EPERM;
+	}
+	// create the last fragment
+	new_packet = packet_get_4_remote(ip_globals.net_phone, prefix, length, suffix, ((addrlen > addr_len) ? addrlen : addr_len));
+	if(! new_packet){
+		return ENOMEM;
+	}
+	// allocate as much as originally
+	last_header = (ip_header_ref) packet_suffix(new_packet, IP_HEADER_LENGTH(header));
+	if(! last_header){
+		return ip_release_and_return(packet, ENOMEM);
+	}
+	ip_create_last_header(last_header, header);
+	// trim the unused space
+	if(ERROR_OCCURRED(packet_trim(new_packet, 0, IP_HEADER_LENGTH(header) - IP_HEADER_LENGTH(last_header)))){
+		return ip_release_and_return(packet, ERROR_CODE);
+	}
+	// biggest multiple of 8 lower than content
+	// TODO even fragmentation?
+	length = length &(~ 0x7);// (content / 8) * 8
+	if(ERROR_OCCURRED(ip_fragment_packet_data(packet, new_packet, header, last_header, ((IP_HEADER_DATA_LENGTH(header) - ((length - IP_HEADER_LENGTH(header)) &(~ 0x7))) % ((length - IP_HEADER_LENGTH(last_header)) &(~ 0x7))), src, dest, addrlen))){
+		return ip_release_and_return(packet, ERROR_CODE);
+	}
+	// mark the first as fragmented
+	header->flags |= IPFLAG_MORE_FRAGMENTS;
+	// create middle framgents
+	while(IP_TOTAL_LENGTH(header) > length){
+		new_packet = packet_get_4_remote(ip_globals.net_phone, prefix, length, suffix, ((addrlen >= addr_len) ? addrlen : addr_len));
+		if(! new_packet){
+			return ENOMEM;
+		}
+		middle_header = ip_create_middle_header(new_packet, last_header);
+		if(! middle_header){
+			return ip_release_and_return(packet, ENOMEM);
+		}
+		if(ERROR_OCCURRED(ip_fragment_packet_data(packet, new_packet, header, middle_header, (length - IP_HEADER_LENGTH(middle_header)) &(~ 0x7), src, dest, addrlen))){
+			return ip_release_and_return(packet, ERROR_CODE);
+		}
+	}
+	// finish the first fragment
+	header->header_checksum = IP_HEADER_CHECKSUM(header);
+	return EOK;
+}
+
+int ip_fragment_packet_data(packet_t packet, packet_t new_packet, ip_header_ref header, ip_header_ref new_header, size_t length, const struct sockaddr * src, const struct sockaddr * dest, socklen_t addrlen){
+	ERROR_DECLARE;
+
+	void * data;
+	size_t offset;
+
+	data = packet_suffix(new_packet, length);
+	if(! data){
+		return ENOMEM;
+	}
+	memcpy(data, ((void *) header) + IP_TOTAL_LENGTH(header) - length, length);
+	ERROR_PROPAGATE(packet_trim(packet, 0, length));
+	header->total_length = htons(IP_TOTAL_LENGTH(header) - length);
+	new_header->total_length = htons(IP_HEADER_LENGTH(new_header) + length);
+	offset = IP_FRAGMENT_OFFSET(header) + IP_HEADER_DATA_LENGTH(header);
+	new_header->fragment_offset_high = IP_COMPUTE_FRAGMENT_OFFSET_HIGH(offset);
+	new_header->fragment_offset_low = IP_COMPUTE_FRAGMENT_OFFSET_LOW(offset);
+	new_header->header_checksum = IP_HEADER_CHECKSUM(new_header);
+	ERROR_PROPAGATE(packet_set_addr(new_packet, (const uint8_t *) src, (const uint8_t *) dest, addrlen));
+	return pq_insert_after(packet, new_packet);
+}
+
+ip_header_ref ip_create_middle_header(packet_t packet, ip_header_ref last){
+	ip_header_ref middle;
+
+	middle = (ip_header_ref) packet_suffix(packet, IP_HEADER_LENGTH(last));
+	if(! middle){
+		return NULL;
+	}
+	memcpy(middle, last, IP_HEADER_LENGTH(last));
+	middle->flags |= IPFLAG_MORE_FRAGMENTS;
+	return middle;
+}
+
+void ip_create_last_header(ip_header_ref last, ip_header_ref first){
+	ip_option_ref option;
+	size_t next;
+	size_t length;
+
+	// copy first itself
+	memcpy(last, first, sizeof(ip_header_t));
+	length = sizeof(ip_header_t);
+	next = sizeof(ip_header_t);
+	// process all ip options
+	while(next < first->header_length){
+		option = (ip_option_ref) (((uint8_t *) first) + next);
+		// skip end or noop
+		if((option->type == IPOPT_END) || (option->type == IPOPT_NOOP)){
+			++ next;
+		}else{
+			// copy if said so or skip
+			if(IPOPT_COPIED(option->type)){
+				memcpy(((uint8_t *) last) + length, ((uint8_t *) first) + next, option->length);
+				length += option->length;
+			}
+			// next option
+			next += option->length;
+		}
+	}
+	// align 4 byte boundary
+	if(length % 4){
+		bzero(((uint8_t *) last) + length, 4 - (length % 4));
+		last->header_length = length / 4 + 1;
+	}else{
+		last->header_length = length / 4;
+	}
+	last->header_checksum = 0;
+}
+
+int ip_receive_message(device_id_t device_id, packet_t packet){
+	packet_t next;
+
+	do{
+		next = pq_detach(packet);
+		ip_process_packet(device_id, packet);
+		packet = next;
+	}while(packet);
+	return EOK;
+}
+
+int ip_process_packet(device_id_t device_id, packet_t packet){
+	ERROR_DECLARE;
+
+	ip_header_ref header;
+	in_addr_t dest;
+	ip_route_ref route;
+	int phone;
+	struct sockaddr * addr;
+	struct sockaddr_in addr_in;
+//	struct sockaddr_in	addr_in6;
+	socklen_t addrlen;
+
+	header = (ip_header_ref) packet_get_data(packet);
+	if(! header){
+		return ip_release_and_return(packet, ENOMEM);
+	}
+	// checksum
+	if((header->header_checksum) && (IP_HEADER_CHECKSUM(header) != IP_CHECKSUM_ZERO)){
+		phone = ip_prepare_icmp_and_get_phone(0, packet, header);
+		if(phone >= 0){
+			// checksum error ICMP
+			icmp_parameter_problem_msg(phone, ICMP_PARAM_POINTER, ((size_t) ((void *) &header->header_checksum)) - ((size_t) ((void *) header)), packet);
+		}
+		return EINVAL;
+	}
+	if(header->ttl <= 1){
+		phone = ip_prepare_icmp_and_get_phone(0, packet, header);
+		if(phone >= 0){
+			// ttl oxceeded ICMP
+			icmp_time_exceeded_msg(phone, ICMP_EXC_TTL, packet);
+		}
+		return EINVAL;
+	}
+	// process ipopt and get destination
+	dest = ip_get_destination(header);
+	// set the addrination address
+	switch(header->version){
+		case IPVERSION:
+			addrlen = sizeof(addr_in);
+			bzero(&addr_in, addrlen);
+			addr_in.sin_family = AF_INET;
+			memcpy(&addr_in.sin_addr.s_addr, &dest, sizeof(dest));
+			addr = (struct sockaddr *) &addr_in;
+			break;
+/*		case IPv6VERSION:
+			addrlen = sizeof(dest_in6);
+			bzero(&dest_in6, addrlen);
+			dest_in6.sin6_family = AF_INET6;
+			memcpy(&dest_in6.sin6_addr.s6_addr,);
+			dest = (struct sockaddr *) &dest_in;
+			break;
+*/		default:
+			return ip_release_and_return(packet, EAFNOSUPPORT);
+	}
+	ERROR_PROPAGATE(packet_set_addr(packet, NULL, (uint8_t *) &addr, addrlen));
+	route = ip_find_route(dest);
+	if(! route){
+		phone = ip_prepare_icmp_and_get_phone(0, packet, header);
+		if(phone >= 0){
+			// unreachable ICMP
+			icmp_destination_unreachable_msg(phone, ICMP_HOST_UNREACH, 0, packet);
+		}
+		return ENOENT;
+	}
+	if(route->address.s_addr == dest.s_addr){
+		// local delivery
+		return ip_deliver_local(device_id, packet, header, 0);
+	}else{
+		// only if routing enabled
+		if(route->netif->routing){
+			-- header->ttl;
+			return ip_send_route(packet, route->netif, route, NULL, dest, 0);
+		}else{
+			phone = ip_prepare_icmp_and_get_phone(0, packet, header);
+			if(phone >= 0){
+				// unreachable ICMP if no routing
+				icmp_destination_unreachable_msg(phone, ICMP_HOST_UNREACH, 0, packet);
+			}
+			return ENOENT;
+		}
+	}
+}
+
+/** Notify the IP module about the received error notification packet.
+ *
+ * @param[in] ip_phone  The IP module phone used for (semi)remote calls.
+ * @param[in] device_id The device identifier.
+ * @param[in] packet    The received packet or the received packet queue.
+ * @param[in] target    The target internetwork module service to be
+ *                      delivered to.
+ * @param[in] error     The packet error reporting service. Prefixes the
+ *                      received packet.
+ *
+ * @return EOK on success.
+ *
+ */
+int ip_received_error_msg_local(int ip_phone, device_id_t device_id, packet_t packet, services_t target, services_t error){
+	uint8_t * data;
+	int offset;
+	icmp_type_t type;
+	icmp_code_t code;
+	ip_netif_ref netif;
+	measured_string_t address;
+	ip_route_ref route;
+	ip_header_ref header;
+
+	switch(error){
+		case SERVICE_ICMP:
+			offset = icmp_client_process_packet(packet, &type, &code, NULL, NULL);
+			if(offset < 0){
+				return ip_release_and_return(packet, ENOMEM);
+			}
+			data = packet_get_data(packet);
+			header = (ip_header_ref)(data + offset);
+			// destination host unreachable?
+			if((type == ICMP_DEST_UNREACH) && (code == ICMP_HOST_UNREACH)){
+				fibril_rwlock_read_lock(&ip_globals.netifs_lock);
+				netif = ip_netifs_find(&ip_globals.netifs, device_id);
+				if(netif && netif->arp){
+					route = ip_routes_get_index(&netif->routes, 0);
+					// from the same network?
+					if(route && ((route->address.s_addr &route->netmask.s_addr) == (header->destination_address &route->netmask.s_addr))){
+						// clear the ARP mapping if any
+						address.value = (char *) &header->destination_address;
+						address.length = CONVERT_SIZE(uint8_t, char, sizeof(header->destination_address));
+						arp_clear_address_req(netif->arp->phone, netif->device_id, SERVICE_IP, &address);
+					}
+				}
+				fibril_rwlock_read_unlock(&ip_globals.netifs_lock);
+			}
+			break;
+		default:
+			return ip_release_and_return(packet, ENOTSUP);
+	}
+	return ip_deliver_local(device_id, packet, header, error);
+}
+
+int ip_deliver_local(device_id_t device_id, packet_t packet, ip_header_ref header, services_t error){
+	ERROR_DECLARE;
+
+	ip_proto_ref proto;
+	int phone;
+	services_t service;
+	tl_received_msg_t received_msg;
+	struct sockaddr * src;
+	struct sockaddr * dest;
+	struct sockaddr_in src_in;
+	struct sockaddr_in dest_in;
+//	struct sockaddr_in	src_in6;
+//	struct sockaddr_in	dest_in6;
+	socklen_t addrlen;
+
+	if((header->flags &IPFLAG_MORE_FRAGMENTS) || IP_FRAGMENT_OFFSET(header)){
+		// TODO fragmented
+		return ENOTSUP;
+	}else{
+		switch(header->version){
+			case IPVERSION:
+				addrlen = sizeof(src_in);
+				bzero(&src_in, addrlen);
+				src_in.sin_family = AF_INET;
+				memcpy(&dest_in, &src_in, addrlen);
+				memcpy(&src_in.sin_addr.s_addr, &header->source_address, sizeof(header->source_address));
+				memcpy(&dest_in.sin_addr.s_addr, &header->destination_address, sizeof(header->destination_address));
+				src = (struct sockaddr *) &src_in;
+				dest = (struct sockaddr *) &dest_in;
+				break;
+/*			case IPv6VERSION:
+				addrlen = sizeof(src_in6);
+				bzero(&src_in6, addrlen);
+				src_in6.sin6_family = AF_INET6;
+				memcpy(&dest_in6, &src_in6, addrlen);
+				memcpy(&src_in6.sin6_addr.s6_addr,);
+				memcpy(&dest_in6.sin6_addr.s6_addr,);
+				src = (struct sockaddr *) &src_in;
+				dest = (struct sockaddr *) &dest_in;
+				break;
+*/			default:
+				return ip_release_and_return(packet, EAFNOSUPPORT);
+		}
+		if(ERROR_OCCURRED(packet_set_addr(packet, (uint8_t *) src, (uint8_t *) dest, addrlen))){
+			return ip_release_and_return(packet, ERROR_CODE);
+		}
+		// trim padding if present
+		if((! error) && (IP_TOTAL_LENGTH(header) < packet_get_data_length(packet))){
+			if(ERROR_OCCURRED(packet_trim(packet, 0, packet_get_data_length(packet) - IP_TOTAL_LENGTH(header)))){
+				return ip_release_and_return(packet, ERROR_CODE);
+			}
+		}
+		fibril_rwlock_read_lock(&ip_globals.protos_lock);
+		proto = ip_protos_find(&ip_globals.protos, header->protocol);
+		if(! proto){
+			fibril_rwlock_read_unlock(&ip_globals.protos_lock);
+			phone = ip_prepare_icmp_and_get_phone(error, packet, header);
+			if(phone >= 0){
+				// unreachable ICMP
+				icmp_destination_unreachable_msg(phone, ICMP_PROT_UNREACH, 0, packet);
+			}
+			return ENOENT;
+		}
+		if(proto->received_msg){
+			service = proto->service;
+			received_msg = proto->received_msg;
+			fibril_rwlock_read_unlock(&ip_globals.protos_lock);
+			ERROR_CODE = received_msg(device_id, packet, service, error);
+		}else{
+			ERROR_CODE = tl_received_msg(proto->phone, device_id, packet, proto->service, error);
+			fibril_rwlock_read_unlock(&ip_globals.protos_lock);
+		}
+		return ERROR_CODE;
+	}
+}
+
+in_addr_t ip_get_destination(ip_header_ref header){
+	in_addr_t destination;
+
+	// TODO search set ipopt route?
+	destination.s_addr = header->destination_address;
+	return destination;
+}
+
+int ip_prepare_icmp(packet_t packet, ip_header_ref header){
+	packet_t next;
+	struct sockaddr * dest;
+	struct sockaddr_in dest_in;
+//	struct sockaddr_in	dest_in6;
+	socklen_t addrlen;
+
+	// detach the first packet and release the others
+	next = pq_detach(packet);
+	if(next){
+		pq_release_remote(ip_globals.net_phone, packet_get_id(next));
+	}
+	if(! header){
+		if(packet_get_data_length(packet) <= sizeof(ip_header_t)){
+			return ENOMEM;
+		}
+		// get header
+		header = (ip_header_ref) packet_get_data(packet);
+		if(! header){
+			return EINVAL;
+		}
+	}
+	// only for the first fragment
+	if(IP_FRAGMENT_OFFSET(header)){
+		return EINVAL;
+	}
+	// not for the ICMP protocol
+	if(header->protocol == IPPROTO_ICMP){
+		return EPERM;
+	}
+	// set the destination address
+	switch(header->version){
+		case IPVERSION:
+			addrlen = sizeof(dest_in);
+			bzero(&dest_in, addrlen);
+			dest_in.sin_family = AF_INET;
+			memcpy(&dest_in.sin_addr.s_addr, &header->source_address, sizeof(header->source_address));
+			dest = (struct sockaddr *) &dest_in;
+			break;
+/*		case IPv6VERSION:
+			addrlen = sizeof(dest_in6);
+			bzero(&dest_in6, addrlen);
+			dest_in6.sin6_family = AF_INET6;
+			memcpy(&dest_in6.sin6_addr.s6_addr,);
+			dest = (struct sockaddr *) &dest_in;
+			break;
+*/		default:
+			return EAFNOSUPPORT;
+	}
+	return packet_set_addr(packet, NULL, (uint8_t *) dest, addrlen);
+}
+
+int ip_get_icmp_phone(void){
+	ip_proto_ref proto;
+	int phone;
+
+	fibril_rwlock_read_lock(&ip_globals.protos_lock);
+	proto = ip_protos_find(&ip_globals.protos, IPPROTO_ICMP);
+	phone = proto ? proto->phone : ENOENT;
+	fibril_rwlock_read_unlock(&ip_globals.protos_lock);
+	return phone;
+}
+
+int ip_prepare_icmp_and_get_phone(services_t error, packet_t packet, ip_header_ref header){
+	int phone;
+
+	phone = ip_get_icmp_phone();
+	if(error || (phone < 0) || ip_prepare_icmp(packet, header)){
+		return ip_release_and_return(packet, EINVAL);
+	}
+	return phone;
+}
+
+int ip_release_and_return(packet_t packet, int result){
+	pq_release_remote(ip_globals.net_phone, packet_get_id(packet));
+	return result;
+}
+
+int ip_get_route_req_local(int ip_phone, ip_protocol_t protocol, const struct sockaddr * destination, socklen_t addrlen, device_id_t * device_id, void **header, size_t * headerlen){
+	struct sockaddr_in * address_in;
+//	struct sockaddr_in6 *	address_in6;
+	in_addr_t * dest;
+	in_addr_t * src;
+	ip_route_ref route;
+	ipv4_pseudo_header_ref header_in;
+
+	if(!(destination && (addrlen > 0))){
+		return EINVAL;
+	}
+	if(!(device_id && header && headerlen)){
+		return EBADMEM;
+	}
+	if((size_t) addrlen < sizeof(struct sockaddr)){
+		return EINVAL;
+	}
+	switch(destination->sa_family){
+		case AF_INET:
+			if(addrlen != sizeof(struct sockaddr_in)){
+				return EINVAL;
+			}
+			address_in = (struct sockaddr_in *) destination;
+			dest = &address_in->sin_addr;
+			if(! dest->s_addr){
+				dest->s_addr = IPV4_LOCALHOST_ADDRESS;
+			}
+			break;
+		// TODO IPv6
+/*		case AF_INET6:
+			if(addrlen != sizeof(struct sockaddr_in6)){
+				return EINVAL;
+			}
+			address_in6 = (struct sockaddr_in6 *) dest;
+			address_in6.sin6_addr.s6_addr;
+*/		default:
+			return EAFNOSUPPORT;
+	}
+	fibril_rwlock_read_lock(&ip_globals.lock);
+	route = ip_find_route(*dest);
+	// if the local host is the destination
+	if(route && (route->address.s_addr == dest->s_addr)
+		&& (dest->s_addr != IPV4_LOCALHOST_ADDRESS)){
+		// find the loopback device to deliver
+		dest->s_addr = IPV4_LOCALHOST_ADDRESS;
+		route = ip_find_route(*dest);
+	}
+	if(!(route && route->netif)){
+		fibril_rwlock_read_unlock(&ip_globals.lock);
+		return ENOENT;
+	}
+	*device_id = route->netif->device_id;
+	src = ip_netif_address(route->netif);
+	fibril_rwlock_read_unlock(&ip_globals.lock);
+	*headerlen = sizeof(*header_in);
+	header_in = (ipv4_pseudo_header_ref) malloc(*headerlen);
+	if(! header_in){
+		return ENOMEM;
+	}
+	bzero(header_in, * headerlen);
+	header_in->destination_address = dest->s_addr;
+	header_in->source_address = src->s_addr;
+	header_in->protocol = protocol;
+	header_in->data_length = 0;
+	*header = header_in;
+	return EOK;
+}
+
 /** Default thread for new connections.
  *
- * @param[in] iid	The initial message identifier.
- * @param[in] icall	The initial message call structure.
- */
-static void il_client_connection(ipc_callid_t iid, ipc_call_t *icall)
+ *  @param[in] iid The initial message identifier.
+ *  @param[in] icall The initial message call structure.
+ *
+ */
+static void il_client_connection(ipc_callid_t iid, ipc_call_t * icall)
 {
 	/*
@@ -2006,5 +1673,5 @@
 	ipc_answer_0(iid, EOK);
 	
-	while (true) {
+	while(true) {
 		ipc_call_t answer;
 		int answer_count;
@@ -2021,12 +1688,7 @@
 		    &answer_count);
 		
-		/*
-		 * End if told to either by the message or the processing
-		 * result.
-		 */
-		if ((IPC_GET_METHOD(call) == IPC_M_PHONE_HUNGUP) ||
-		    (res == EHANGUP)) {
+		/* End if said to either by the message or the processing result */
+		if ((IPC_GET_METHOD(call) == IPC_M_PHONE_HUNGUP) || (res == EHANGUP))
 			return;
-		}
 		
 		/* Answer the message */
@@ -2037,14 +1699,20 @@
 /** Starts the module.
  *
- * @returns EOK on success.
- * @returns Other error codes as defined for each specific module start function.
+ *  @param argc The count of the command line arguments. Ignored parameter.
+ *  @param argv The command line parameters. Ignored parameter.
+ *
+ *  @returns EOK on success.
+ *  @returns Other error codes as defined for each specific module start function.
+ *
  */
 int main(int argc, char *argv[])
 {
-	int rc;
+	ERROR_DECLARE;
 	
 	/* Start the module */
-	rc = il_module_start_standalone(il_client_connection);
-	return rc;
+	if (ERROR_OCCURRED(il_module_start_standalone(il_client_connection)))
+		return ERROR_CODE;
+	
+	return EOK;
 }
 
