Index: uspace/srv/inet/addrobj.c
===================================================================
--- uspace/srv/inet/addrobj.c	(revision ceba4bed90274ab7e0275c4ddcb524984cc64a39)
+++ uspace/srv/inet/addrobj.c	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -90,6 +90,11 @@
 }
 
-/** Find address object matching address @a addr */
-inet_addrobj_t *inet_addrobj_find(inet_addr_t *addr)
+/** Find address object matching address @a addr.
+ *
+ * @param addr	Address
+ * @oaram find	iaf_net to find network (using mask),
+ *		iaf_addr to find local address (exact match)
+ */
+inet_addrobj_t *inet_addrobj_find(inet_addr_t *addr, inet_addrobj_find_t find)
 {
 	uint32_t mask;
Index: uspace/srv/inet/addrobj.h
===================================================================
--- uspace/srv/inet/addrobj.h	(revision ceba4bed90274ab7e0275c4ddcb524984cc64a39)
+++ uspace/srv/inet/addrobj.h	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -40,9 +40,16 @@
 #include "inet.h"
 
+typedef enum {
+	/* Find matching network address (using mask) */
+	iaf_net,
+	/* Find exact local address (not using mask) */
+	iaf_addr
+} inet_addrobj_find_t;
+
 extern inet_addrobj_t *inet_addrobj_new(void);
 extern void inet_addrobj_delete(inet_addrobj_t *);
 extern void inet_addrobj_add(inet_addrobj_t *);
 extern void inet_addrobj_remove(inet_addrobj_t *);
-extern inet_addrobj_t *inet_addrobj_find(inet_addr_t *);
+extern inet_addrobj_t *inet_addrobj_find(inet_addr_t *, inet_addrobj_find_t);
 extern int inet_addrobj_send_dgram(inet_addrobj_t *, inet_dgram_t *,
     uint8_t ttl, int df);
Index: uspace/srv/inet/inet.c
===================================================================
--- uspace/srv/inet/inet.c	(revision ceba4bed90274ab7e0275c4ddcb524984cc64a39)
+++ uspace/srv/inet/inet.c	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -101,10 +101,9 @@
 }
 
-static int inet_send(inet_client_t *client, inet_dgram_t *dgram,
-    uint8_t ttl, int df)
+static int inet_route_packet(inet_dgram_t *dgram, uint8_t ttl, int df)
 {
 	inet_addrobj_t *addr;
 
-	addr = inet_addrobj_find(&dgram->dest);
+	addr = inet_addrobj_find(&dgram->dest, iaf_net);
 	if (addr != NULL) {
 		/* Destination is directly accessible */
@@ -115,4 +114,10 @@
 	log_msg(LVL_DEBUG, "inet_send: No route to destination.");
 	return ENOENT;
+}
+
+static int inet_send(inet_client_t *client, inet_dgram_t *dgram,
+    uint8_t ttl, int df)
+{
+	return inet_route_packet(dgram, ttl, df);
 }
 
@@ -255,4 +260,9 @@
 }
 
+int inet_recv_packet(inet_dgram_t *dgram, uint8_t ttl, int df)
+{
+	return inet_route_packet(dgram, ttl, df);
+}
+
 int main(int argc, char *argv[])
 {
Index: uspace/srv/inet/inet.h
===================================================================
--- uspace/srv/inet/inet.h	(revision ceba4bed90274ab7e0275c4ddcb524984cc64a39)
+++ uspace/srv/inet/inet.h	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -87,4 +87,5 @@
 
 extern int inet_ev_recv(inet_client_t *, inet_dgram_t *);
+extern int inet_recv_packet(inet_dgram_t *, uint8_t ttl, int df);
 
 #endif
Index: uspace/srv/inet/inet_link.c
===================================================================
--- uspace/srv/inet/inet_link.c	(revision ceba4bed90274ab7e0275c4ddcb524984cc64a39)
+++ uspace/srv/inet/inet_link.c	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -58,8 +58,17 @@
 static FIBRIL_MUTEX_INITIALIZE(inet_discovery_lock);
 
-static int inet_iplink_recv(iplink_t *ilink, iplink_sdu_t *sdu)
-{
+static int inet_iplink_recv(iplink_t *iplink, iplink_sdu_t *sdu)
+{
+	inet_dgram_t dgram;
+	uint8_t ttl;
+	int df;
+	int rc;
+
 	log_msg(LVL_DEBUG, "inet_iplink_recv()");
-	return EOK;
+	rc = inet_pdu_decode(sdu->data, sdu->size, &dgram, &ttl, &df);
+	if (rc != EOK)
+		return rc;
+
+	return inet_recv_packet(&dgram, ttl, df);
 }
 
@@ -211,5 +220,5 @@
 	sdu.lsrc.ipv4 = lsrc->ipv4;
 	sdu.ldest.ipv4 = ldest->ipv4;
-	rc = inet_pdu_encode(dgram, &sdu.data, &sdu.size);
+	rc = inet_pdu_encode(dgram, ttl, df, &sdu.data, &sdu.size);
 	if (rc != EOK)
 		return rc;
Index: uspace/srv/inet/inet_std.h
===================================================================
--- uspace/srv/inet/inet_std.h	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
+++ uspace/srv/inet/inet_std.h	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2012 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup inet
+ * @{
+ */
+/**
+ * @file IP header definitions
+ *
+ */
+
+#ifndef INET_STD_H_
+#define INET_STD_H_
+
+#include <sys/types.h>
+
+/** Internet Datagram header (fixed part) */
+typedef struct {
+	/** Version, Internet Header Length */
+	uint8_t ver_ihl;
+	/* Type of Service */
+	uint8_t tos;
+	/** Total Length */
+	uint16_t tot_len;
+	/** Identification */
+	uint16_t id;
+	/** Flags, Fragment Offset */
+	uint16_t flags_foff;
+	/** Time to Live */
+	uint8_t ttl;
+	/** Protocol */
+	uint8_t proto;
+	/** Header Checksum */
+	uint16_t chksum;
+	/** Source Address */
+	uint32_t src_addr;
+	/** Destination Address */
+	uint32_t dest_addr;
+} ip_header_t;
+
+/** Bits in ip_header_t.ver_ihl */
+enum ver_ihl_bits {
+	/** Version, highest bit */
+	VI_VERSION_h = 7,
+	/** Version, lowest bit */
+	VI_VERSION_l = 4,
+	/** Internet Header Length, highest bit */
+	VI_IHL_h     = 3,
+	/** Internet Header Length, lowest bit */
+	VI_IHL_l     = 0
+};
+
+/** Bits in ip_header_t.flags_foff */
+enum flags_foff_bits {
+	/** Reserved, must be zero */
+	FF_FLAG_RSVD = 15,
+	/** Don't Fragment */
+	FF_FLAG_DF = 14,
+	/** More Fragments */
+	FF_FLAG_MF = 13,
+	/** Fragment Offset, highest bit */
+	FF_FRAGOFF_h = 12,
+	/** Fragment Offset, lowest bit */
+	FF_FRAGOFF_l = 0
+};
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/inet/pdu.c
===================================================================
--- uspace/srv/inet/pdu.c	(revision ceba4bed90274ab7e0275c4ddcb524984cc64a39)
+++ uspace/srv/inet/pdu.c	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -35,8 +35,14 @@
  */
 
+#include <align.h>
+#include <bitops.h>
+#include <byteorder.h>
 #include <errno.h>
+#include <io/log.h>
+#include <mem.h>
 #include <stdlib.h>
 
 #include "inet.h"
+#include "inet_std.h"
 #include "pdu.h"
 
@@ -45,15 +51,34 @@
  * XXX We should be encoding from packet, not from datagram.
  */
-int inet_pdu_encode(inet_dgram_t *dgram, void **rdata, size_t *rsize)
+int inet_pdu_encode(inet_dgram_t *dgram, uint8_t ttl, int df, void **rdata,
+    size_t *rsize)
 {
 	void *data;
 	size_t size;
+	ip_header_t *hdr;
+	size_t hdr_size;
+	size_t data_offs;
 
-	size = 20;
+	hdr_size = sizeof(ip_header_t);
+	size = hdr_size + dgram->size;
+	data_offs = ROUND_UP(hdr_size, 4);
+
 	data = calloc(size, 1);
 	if (data == NULL)
 		return ENOMEM;
 
-	/* XXX Implement */
+	hdr = (ip_header_t *)data;
+	hdr->ver_ihl = (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
+	hdr->tos = dgram->tos;
+	hdr->tot_len = host2uint16_t_be(size);
+	hdr->id = host2uint16_t_be(42);
+	hdr->flags_foff = host2uint16_t_be(df ? BIT_V(uint16_t, FF_FLAG_DF) : 0);
+	hdr->ttl = ttl;
+	hdr->proto = 0;
+	hdr->chksum = 0;
+	hdr->src_addr = host2uint32_t_be(dgram->src.ipv4);
+	hdr->dest_addr = host2uint32_t_be(dgram->dest.ipv4);
+
+	memcpy((uint8_t *)data + data_offs, dgram->data, dgram->size);
 
 	*rdata = data;
@@ -62,4 +87,71 @@
 }
 
+int inet_pdu_decode(void *data, size_t size, inet_dgram_t *dgram, uint8_t *ttl,
+    int *df)
+{
+	ip_header_t *hdr;
+	size_t tot_len;
+	size_t data_offs;
+	uint8_t version;
+	uint16_t ident;
+
+	log_msg(LVL_DEBUG, "inet_pdu_decode()");
+
+	if (size < sizeof(ip_header_t)) {
+		log_msg(LVL_DEBUG, "PDU too short (%zu)", size);
+		return EINVAL;
+	}
+
+	hdr = (ip_header_t *)data;
+
+	version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h, VI_VERSION_l,
+	    hdr->ver_ihl);
+	if (version != 4) {
+		log_msg(LVL_DEBUG, "Version (%d) != 4", version);
+		return EINVAL;
+	}
+
+	tot_len = uint16_t_be2host(hdr->tot_len);
+	if (tot_len < sizeof(ip_header_t)) {
+		log_msg(LVL_DEBUG, "Total Length too small (%zu)", tot_len);
+		return EINVAL;
+	}
+
+	if (tot_len > size) {
+		log_msg(LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
+			tot_len, size);
+		return EINVAL;
+	}
+
+	ident = uint16_t_be2host(hdr->id);
+	(void)ident;
+	/* XXX Flags */
+	/* XXX Fragment offset */
+	/* XXX Protocol */
+	/* XXX Checksum */
+
+	dgram->src.ipv4 = uint32_t_be2host(hdr->src_addr);
+	dgram->dest.ipv4 = uint32_t_be2host(hdr->dest_addr);
+	dgram->tos = hdr->tos;
+	*ttl = hdr->ttl;
+	*df = (uint16_t_be2host(hdr->tos) & BIT_V(uint16_t, FF_FLAG_DF)) ?
+	    1 : 0;
+
+	/* XXX IP options */
+	data_offs = sizeof(uint32_t) * BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h,
+	    VI_IHL_l, hdr->ver_ihl);
+
+	dgram->size = tot_len - data_offs;
+	dgram->data = calloc(dgram->size, 1);
+	if (dgram->data == NULL) {
+		log_msg(LVL_WARN, "Out of memory.");
+		return ENOMEM;
+	}
+
+	memcpy(dgram->data, (uint8_t *)data + data_offs, dgram->size);
+
+	return EOK;
+}
+
 /** @}
  */
Index: uspace/srv/inet/pdu.h
===================================================================
--- uspace/srv/inet/pdu.h	(revision ceba4bed90274ab7e0275c4ddcb524984cc64a39)
+++ uspace/srv/inet/pdu.h	(revision e767dbf7fef8812a5f05b9f22c38adfb40680b98)
@@ -40,5 +40,6 @@
 #include "inet.h"
 
-extern int inet_pdu_encode(inet_dgram_t *, void **, size_t *);
+extern int inet_pdu_encode(inet_dgram_t *, uint8_t, int, void **, size_t *);
+extern int inet_pdu_decode(void *, size_t, inet_dgram_t *, uint8_t *, int *);
 
 #endif
