Index: uspace/srv/net/inetsrv/Makefile
===================================================================
--- uspace/srv/net/inetsrv/Makefile	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/Makefile	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -39,4 +39,6 @@
 	inetping.c \
 	inetping6.c \
+	ndp.c \
+	ntrans.c \
 	pdu.c \
 	reass.c \
Index: uspace/srv/net/inetsrv/addrobj.c
===================================================================
--- uspace/srv/net/inetsrv/addrobj.c	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/addrobj.c	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -42,7 +42,9 @@
 #include <stdlib.h>
 #include <str.h>
+#include <net/socket_codes.h>
 #include "addrobj.h"
 #include "inetsrv.h"
 #include "inet_link.h"
+#include "ndp.h"
 
 static inet_addrobj_t *inet_addrobj_find_by_name_locked(const char *, inet_link_t *);
@@ -214,6 +216,35 @@
 	inet_naddr_addr(&addr->naddr, &lsrc_addr);
 	
-	return inet_link_send_dgram(addr->ilink, &lsrc_addr, ldest, dgram,
-	    proto, ttl, df);
+	addr32_t lsrc_v4;
+	addr128_t lsrc_v6;
+	uint16_t lsrc_af = inet_addr_get(&lsrc_addr, &lsrc_v4, &lsrc_v6);
+	
+	addr32_t ldest_v4;
+	addr128_t ldest_v6;
+	uint16_t ldest_af = inet_addr_get(ldest, &ldest_v4, &ldest_v6);
+	
+	if (lsrc_af != ldest_af)
+		return EINVAL;
+	
+	int rc;
+	addr48_t ldest_mac;
+	
+	switch (ldest_af) {
+	case AF_INET:
+		return inet_link_send_dgram(addr->ilink, lsrc_v4, ldest_v4,
+		    dgram, proto, ttl, df);
+	case AF_INET6:
+		/*
+		 * Translate local destination IPv6 address.
+		 */
+		rc = ndp_translate(lsrc_v6, ldest_v6, ldest_mac, addr->ilink);
+		if (rc != EOK)
+			return rc;
+		
+		return inet_link_send_dgram6(addr->ilink, ldest_mac, dgram,
+		    proto, ttl, df);
+	}
+	
+	return ENOTSUP;
 }
 
Index: uspace/srv/net/inetsrv/icmpv6.c
===================================================================
--- uspace/srv/net/inetsrv/icmpv6.c	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/icmpv6.c	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -47,10 +47,4 @@
 #include "pdu.h"
 
-static int ndp_received(inet_dgram_t *dgram)
-{
-	// FIXME TODO
-	return ENOTSUP;
-}
-
 static int icmpv6_recv_echo_request(inet_dgram_t *dgram)
 {
Index: uspace/srv/net/inetsrv/icmpv6_std.h
===================================================================
--- uspace/srv/net/inetsrv/icmpv6_std.h	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/icmpv6_std.h	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -47,4 +47,8 @@
 #define INET6_HOP_LIMIT_MAX  255
 
+#define NDP_FLAG_ROUTER     0x80
+#define NDP_FLAG_OVERRIDE   0x40
+#define NDP_FLAG_SOLICITED  0x20
+
 /** ICMPv6 message type */
 enum icmpv6_type {
@@ -83,5 +87,5 @@
 			uint8_t flags;
 			/** Reserved bytes */
-			uint8_t reserved [3];
+			uint8_t reserved[3];
 		} ndp;
 	} un;
@@ -91,11 +95,11 @@
 typedef struct {
 	/** Source IPv6 address */
-	uint8_t src_addr [16];
+	uint8_t src_addr[16];
 	/** Target IPv6 address */
-	uint8_t dest_addr [16];
+	uint8_t dest_addr[16];
 	/** ICMPv6 length */
 	uint32_t length;
 	/** Zeroes */
-	uint8_t zeroes [3];
+	uint8_t zeroes[3];
 	/** Next header */
 	uint8_t next;
@@ -105,5 +109,5 @@
 typedef struct {
 	/** Target IPv6 address */
-	uint8_t target_address [16];
+	uint8_t target_address[16];
 	/** Option code */
 	uint8_t option;
@@ -111,5 +115,5 @@
 	uint8_t length;
 	/** MAC address */
-	uint8_t mac [6];
+	uint8_t mac[6];
 } ndp_message_t;
 
@@ -131,5 +135,5 @@
 	uint32_t reserved;
 	/** Prefix */
-	uint8_t prefix [16];
+	uint8_t prefix[16];
 } ndp_prefix_t;
 
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -201,4 +201,11 @@
 		goto error;
 	}
+	
+	/*
+	 * Get the MAC address of the link. If the link has a MAC
+	 * address, we assume that it supports NDP.
+	 */
+	rc = iplink_get_mac48(ilink->iplink, &ilink->mac);
+	ilink->mac_valid = (rc == EOK);
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Opened IP link '%s'", ilink->svc_name);
@@ -298,12 +305,27 @@
 }
 
-/** Send datagram over Internet link */
-int inet_link_send_dgram(inet_link_t *ilink, inet_addr_t *lsrc,
-    inet_addr_t *ldest, inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
-{
+/** Send IPv4 datagram over Internet link */
+int inet_link_send_dgram(inet_link_t *ilink, addr32_t lsrc, addr32_t ldest,
+    inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
+{
+	addr32_t src_v4;
+	uint16_t src_af = inet_addr_get(&dgram->src, &src_v4, NULL);
+	if (src_af != AF_INET)
+		return EINVAL;
+	
+	addr32_t dest_v4;
+	uint16_t dest_af = inet_addr_get(&dgram->dest, &dest_v4, NULL);
+	if (dest_af != AF_INET)
+		return EINVAL;
+	
 	/*
 	 * Fill packet structure. Fragmentation is performed by
 	 * inet_pdu_encode().
 	 */
+	
+	iplink_sdu_t sdu;
+	
+	sdu.src = lsrc;
+	sdu.dest = ldest;
 	
 	inet_packet_t packet;
@@ -318,16 +340,13 @@
 	packet.size = dgram->size;
 	
-	iplink_sdu_t sdu;
 	size_t offs = 0;
 	int rc;
-	
-	sdu.src = *lsrc;
-	sdu.dest = *ldest;
 	
 	do {
 		/* Encode one fragment */
+		
 		size_t roffs;
-		rc = inet_pdu_encode(&packet, offs, ilink->def_mtu, &sdu.data,
-		    &sdu.size, &roffs);
+		rc = inet_pdu_encode(&packet, src_v4, dest_v4, offs, ilink->def_mtu,
+		    &sdu.data, &sdu.size, &roffs);
 		if (rc != EOK)
 			return rc;
@@ -335,6 +354,61 @@
 		/* Send the PDU */
 		rc = iplink_send(ilink->iplink, &sdu);
+		
 		free(sdu.data);
-		
+		offs = roffs;
+	} while (offs < packet.size);
+	
+	return rc;
+}
+
+/** Send IPv6 datagram over Internet link */
+int inet_link_send_dgram6(inet_link_t *ilink, addr48_t ldest,
+    inet_dgram_t *dgram, uint8_t proto, uint8_t ttl, int df)
+{
+	addr128_t src_v6;
+	uint16_t src_af = inet_addr_get(&dgram->src, NULL, &src_v6);
+	if (src_af != AF_INET6)
+		return EINVAL;
+	
+	addr128_t dest_v6;
+	uint16_t dest_af = inet_addr_get(&dgram->dest, NULL, &dest_v6);
+	if (dest_af != AF_INET6)
+		return EINVAL;
+	
+	iplink_sdu6_t sdu6;
+	addr48(ldest, sdu6.dest);
+	
+	/*
+	 * Fill packet structure. Fragmentation is performed by
+	 * inet_pdu_encode6().
+	 */
+	
+	inet_packet_t packet;
+	
+	packet.src = dgram->src;
+	packet.dest = dgram->dest;
+	packet.tos = dgram->tos;
+	packet.proto = proto;
+	packet.ttl = ttl;
+	packet.df = df;
+	packet.data = dgram->data;
+	packet.size = dgram->size;
+	
+	int rc;
+	size_t offs = 0;
+	
+	do {
+		/* Encode one fragment */
+		
+		size_t roffs;
+		rc = inet_pdu_encode6(&packet, src_v6, dest_v6, offs, ilink->def_mtu,
+		    &sdu6.data, &sdu6.size, &roffs);
+		if (rc != EOK)
+			return rc;
+		
+		/* Send the PDU */
+		rc = iplink_send6(ilink->iplink, &sdu6);
+		
+		free(sdu6.data);
 		offs = roffs;
 	} while (offs < packet.size);
Index: uspace/srv/net/inetsrv/inet_link.h
===================================================================
--- uspace/srv/net/inetsrv/inet_link.h	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/inet_link.h	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -42,6 +42,8 @@
 
 extern int inet_link_discovery_start(void);
-extern int inet_link_send_dgram(inet_link_t *, inet_addr_t *,
-    inet_addr_t *, inet_dgram_t *, uint8_t, uint8_t, int);
+extern int inet_link_send_dgram(inet_link_t *, addr32_t,
+    addr32_t, inet_dgram_t *, uint8_t, uint8_t, int);
+extern int inet_link_send_dgram6(inet_link_t *, addr48_t, inet_dgram_t *,
+    uint8_t, uint8_t, int);
 extern inet_link_t *inet_link_get_by_id(sysarg_t);
 
Index: uspace/srv/net/inetsrv/inetping6.c
===================================================================
--- uspace/srv/net/inetsrv/inetping6.c	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/inetping6.c	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -109,5 +109,5 @@
 	aid_t req = async_send_1(exch, INETPING6_EV_RECV, sdu->seq_no, &answer);
 	
-	int rc = async_data_write_start(exch, sdu->src, 16);
+	int rc = async_data_write_start(exch, sdu->src, sizeof(addr128_t));
 	if (rc != EOK) {
 		async_exchange_end(exch);
@@ -116,5 +116,5 @@
 	}
 	
-	rc = async_data_write_start(exch, sdu->dest, 16);
+	rc = async_data_write_start(exch, sdu->dest, sizeof(addr128_t));
 	if (rc != EOK) {
 		async_exchange_end(exch);
Index: uspace/srv/net/inetsrv/inetsrv.h
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.h	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/inetsrv.h	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -141,4 +141,6 @@
 	iplink_t *iplink;
 	size_t def_mtu;
+	addr48_t mac;
+	bool mac_valid;
 } inet_link_t;
 
Index: uspace/srv/net/inetsrv/pdu.c
===================================================================
--- uspace/srv/net/inetsrv/pdu.c	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/pdu.c	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -88,5 +88,5 @@
 }
 
-/** Encode Internet PDU.
+/** Encode IPv4 PDU.
  *
  * Encode internet packet into PDU (serialized form). Will encode a
@@ -96,25 +96,17 @@
  * be set in the header, otherwise the offset will equal @a packet->size.
  *
- * @param packet	Packet to encode
- * @param offs		Offset into packet payload (in bytes)
- * @param mtu		MTU (Maximum Transmission Unit) in bytes
- * @param rdata		Place to store pointer to allocated data buffer
- * @param rsize		Place to store size of allocated data buffer
- * @param roffs		Place to store offset of remaning data
- */
-int inet_pdu_encode(inet_packet_t *packet, size_t offs, size_t mtu,
-    void **rdata, size_t *rsize, size_t *roffs)
-{
-	addr32_t src_v4;
-	addr128_t src_v6;
-	uint16_t src_af = inet_addr_get(&packet->src, &src_v4, &src_v6);
-	
-	addr32_t dest_v4;
-	addr128_t dest_v6;
-	uint16_t dest_af = inet_addr_get(&packet->dest, &dest_v4, &dest_v6);
-	
-	if (src_af != dest_af)
-		return EINVAL;
-	
+ * @param packet Packet to encode
+ * @param src    Source address
+ * @param dest   Destination address
+ * @param offs   Offset into packet payload (in bytes)
+ * @param mtu    MTU (Maximum Transmission Unit) in bytes
+ * @param rdata  Place to store pointer to allocated data buffer
+ * @param rsize  Place to store size of allocated data buffer
+ * @param roffs  Place to store offset of remaning data
+ *
+ */
+int inet_pdu_encode(inet_packet_t *packet, addr32_t src, addr32_t dest,
+   size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs)
+{
 	/* Upper bound for fragment offset field */
 	size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
@@ -124,16 +116,5 @@
 		return ELIMIT;
 	
-	size_t hdr_size;
-	
-	switch (src_af) {
-	case AF_INET:
-		hdr_size = sizeof(ip_header_t);
-		break;
-	case AF_INET6:
-		hdr_size = sizeof(ip6_header_t);
-		break;
-	default:
-		assert(false);
-	}
+	size_t hdr_size = sizeof(ip_header_t);
 	
 	size_t data_offs = ROUND_UP(hdr_size, 4);
@@ -177,47 +158,125 @@
 	
 	/* Encode header fields */
-	ip_header_t *hdr;
-	ip6_header_t *hdr6;
-	
-	switch (src_af) {
-	case AF_INET:
-		hdr = (ip_header_t *) data;
-		
-		hdr->ver_ihl =
-		    (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
-		hdr->tos = packet->tos;
-		hdr->tot_len = host2uint16_t_be(size);
-		hdr->id = host2uint16_t_be(ident);
-		hdr->flags_foff = host2uint16_t_be(flags_foff);
-		hdr->ttl = packet->ttl;
-		hdr->proto = packet->proto;
-		hdr->chksum = 0;
-		hdr->src_addr = host2uint32_t_be(src_v4);
-		hdr->dest_addr = host2uint32_t_be(dest_v4);
-		
-		/* Compute checksum */
-		uint16_t chksum = inet_checksum_calc(INET_CHECKSUM_INIT,
-		    (void *) hdr, hdr_size);
-		hdr->chksum = host2uint16_t_be(chksum);
-		
-		break;
-	case AF_INET6:
-		// TODO FIXME: fragmentation
-		
-		hdr6 = (ip6_header_t *) data;
-		
-		hdr6->ver_tc = (6 << (VI_VERSION_l));
-		memset(hdr6->tc_fl, 0, 3);
-		hdr6->payload_len = host2uint16_t_be(packet->size);
-		hdr6->next = packet->proto;
-		hdr6->hop_limit = packet->ttl;
-		
-		host2addr128_t_be(src_v6, hdr6->src_addr);
-		host2addr128_t_be(dest_v6, hdr6->dest_addr);
-		
-		break;
-	default:
-		assert(false);
-	}
+	ip_header_t *hdr = (ip_header_t *) data;
+	
+	hdr->ver_ihl =
+	    (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
+	hdr->tos = packet->tos;
+	hdr->tot_len = host2uint16_t_be(size);
+	hdr->id = host2uint16_t_be(ident);
+	hdr->flags_foff = host2uint16_t_be(flags_foff);
+	hdr->ttl = packet->ttl;
+	hdr->proto = packet->proto;
+	hdr->chksum = 0;
+	hdr->src_addr = host2uint32_t_be(src);
+	hdr->dest_addr = host2uint32_t_be(dest);
+	
+	/* Compute checksum */
+	uint16_t chksum = inet_checksum_calc(INET_CHECKSUM_INIT,
+	    (void *) hdr, hdr_size);
+	hdr->chksum = host2uint16_t_be(chksum);
+	
+	/* Copy payload */
+	memcpy((uint8_t *) data + data_offs, packet->data + offs, xfer_size);
+	
+	*rdata = data;
+	*rsize = size;
+	*roffs = rem_offs;
+	
+	return EOK;
+}
+
+/** Encode IPv6 PDU.
+ *
+ * Encode internet packet into PDU (serialized form). Will encode a
+ * fragment of the payload starting at offset @a offs. The resulting
+ * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset
+ * of remaining payload. If some data is remaining, the MF flag will
+ * be set in the header, otherwise the offset will equal @a packet->size.
+ *
+ * @param packet Packet to encode
+ * @param src    Source address
+ * @param dest   Destination address
+ * @param offs   Offset into packet payload (in bytes)
+ * @param mtu    MTU (Maximum Transmission Unit) in bytes
+ * @param rdata  Place to store pointer to allocated data buffer
+ * @param rsize  Place to store size of allocated data buffer
+ * @param roffs Place to store offset of remaning data
+ *
+ */
+int inet_pdu_encode6(inet_packet_t *packet, addr128_t src, addr128_t dest,
+    size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs)
+{
+	/* Upper bound for fragment offset field */
+	size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
+	
+	/* Verify that total size of datagram is within reasonable bounds */
+	if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
+		return ELIMIT;
+	
+	size_t hdr_size = sizeof(ip6_header_t);
+	
+	size_t data_offs = ROUND_UP(hdr_size, 4);
+	
+	assert(offs % FRAG_OFFS_UNIT == 0);
+	assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
+	
+#if 0
+	// FIXME TODO fragmentation
+	
+	/* Value for the fragment offset field */
+	uint16_t foff = offs / FRAG_OFFS_UNIT;
+#endif
+	
+	if (hdr_size >= mtu)
+		return EINVAL;
+	
+	/* Amount of space in the PDU available for payload */
+	size_t spc_avail = mtu - hdr_size;
+	spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
+	
+	/* Amount of data (payload) to transfer */
+	size_t xfer_size = min(packet->size - offs, spc_avail);
+	
+	/* Total PDU size */
+	size_t size = hdr_size + xfer_size;
+	
+	/* Offset of remaining payload */
+	size_t rem_offs = offs + xfer_size;
+	
+#if 0
+	// FIXME TODO fragmentation
+	
+	/* Flags */
+	uint16_t flags_foff =
+	    (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
+	    (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
+	    (foff << FF_FRAGOFF_l);
+#endif
+	
+	void *data = calloc(size, 1);
+	if (data == NULL)
+		return ENOMEM;
+	
+#if 0
+	// FIXME TODO fragmentation
+	
+	/* Allocate identifier */
+	fibril_mutex_lock(&ip_ident_lock);
+	uint16_t ident = ++ip_ident;
+	fibril_mutex_unlock(&ip_ident_lock);
+#endif
+	
+	/* Encode header fields */
+	ip6_header_t *hdr6 = (ip6_header_t *) data;
+	
+	hdr6->ver_tc = (6 << (VI_VERSION_l));
+	memset(hdr6->tc_fl, 0, 3);
+	hdr6->payload_len = host2uint16_t_be(packet->size);
+	hdr6->next = packet->proto;
+	hdr6->hop_limit = packet->ttl;
+	
+	host2addr128_t_be(src, hdr6->src_addr);
+	host2addr128_t_be(dest, hdr6->dest_addr);
 	
 	/* Copy payload */
@@ -257,5 +316,5 @@
 	if (tot_len > size) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
-			tot_len, size);
+		    tot_len, size);
 		return EINVAL;
 	}
@@ -296,6 +355,153 @@
 int inet_pdu_decode6(void *data, size_t size, inet_packet_t *packet)
 {
-	// FIXME TODO
-	return ENOTSUP;
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode6()");
+	
+	if (size < sizeof(ip6_header_t)) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size);
+		return EINVAL;
+	}
+	
+	ip6_header_t *hdr6 = (ip6_header_t *) data;
+	
+	uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h,
+	    VI_VERSION_l, hdr6->ver_tc);
+	if (version != 6) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 6", version);
+		return EINVAL;
+	}
+	
+	size_t payload_len = uint16_t_be2host(hdr6->payload_len);
+	if (payload_len + sizeof(ip6_header_t) > size) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
+		    payload_len + sizeof(ip6_header_t), size);
+		return EINVAL;
+	}
+	
+#if 0
+	// FIXME TODO fragmentation
+	
+	uint16_t ident = uint16_t_be2host(hdr->id);
+	uint16_t flags_foff = uint16_t_be2host(hdr->flags_foff);
+	uint16_t foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
+	    flags_foff);
+#endif
+	
+	/* XXX Checksum */
+	
+	addr128_t src;
+	addr128_t dest;
+	
+	addr128_t_be2host(hdr6->src_addr, src);
+	inet_addr_set6(src, &packet->src);
+	
+	addr128_t_be2host(hdr6->dest_addr, dest);
+	inet_addr_set6(dest, &packet->dest);
+	
+	packet->tos = 0;
+	packet->proto = hdr6->next;
+	packet->ttl = hdr6->hop_limit;
+	
+#if 0
+	// FIXME TODO fragmentation
+	
+	packet->ident = ident;
+	packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
+	packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
+	packet->offs = foff * FRAG_OFFS_UNIT;
+	
+	/* XXX IP options */
+	size_t data_offs = sizeof(uint32_t) *
+	    BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h, VI_IHL_l, hdr->ver_ihl);
+#endif
+	
+	packet->ident = 0;
+	packet->df = 0;
+	packet->mf = 0;
+	packet->offs = 0;
+	
+	packet->size = payload_len;
+	packet->data = calloc(packet->size, 1);
+	if (packet->data == NULL) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory.");
+		return ENOMEM;
+	}
+	
+	memcpy(packet->data, (uint8_t *) data + sizeof(ip6_header_t), packet->size);
+	
+	return EOK;
+}
+
+int ndp_pdu_encode(ndp_packet_t *ndp, inet_dgram_t *dgram)
+{
+	inet_addr_set6(ndp->sender_proto_addr, &dgram->src);
+	inet_addr_set6(ndp->target_proto_addr, &dgram->dest);
+	dgram->tos = 0;
+	dgram->size = sizeof(icmpv6_message_t) + sizeof(ndp_message_t);
+	
+	dgram->data = calloc(1, dgram->size);
+	if (dgram->data == NULL)
+		return ENOMEM;
+	
+	icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data;
+	
+	icmpv6->type = ndp->opcode;
+	icmpv6->code = 0;
+	memset(icmpv6->un.ndp.reserved, 0, 3);
+	
+	ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1);
+	
+	if (ndp->opcode == ICMPV6_NEIGHBOUR_SOLICITATION) {
+		host2addr128_t_be(ndp->solicited_ip, message->target_address);
+		message->option = 1;
+		icmpv6->un.ndp.flags = 0;
+	} else {
+		host2addr128_t_be(ndp->sender_proto_addr, message->target_address);
+		message->option = 2;
+		icmpv6->un.ndp.flags = NDP_FLAG_OVERRIDE | NDP_FLAG_SOLICITED;
+	}
+	
+	message->length = 1;
+	addr48(ndp->sender_hw_addr, message->mac);
+	
+	icmpv6_pseudo_header phdr;
+	
+	host2addr128_t_be(ndp->sender_proto_addr, phdr.src_addr);
+	host2addr128_t_be(ndp->target_proto_addr, phdr.dest_addr);
+	phdr.length = host2uint32_t_be(dgram->size);
+	memset(phdr.zeroes, 0, 3);
+	phdr.next = IP_PROTO_ICMPV6;
+	
+	uint16_t cs_phdr =
+	    inet_checksum_calc(INET_CHECKSUM_INIT, &phdr,
+	    sizeof(icmpv6_pseudo_header));
+	
+	uint16_t cs_all = inet_checksum_calc(cs_phdr, dgram->data,
+	    dgram->size);
+	
+	icmpv6->checksum = host2uint16_t_be(cs_all);
+	
+	return EOK;
+}
+
+int ndp_pdu_decode(inet_dgram_t *dgram, ndp_packet_t *ndp)
+{
+	uint16_t src_af = inet_addr_get(&dgram->src, NULL,
+	    &ndp->sender_proto_addr);
+	if (src_af != AF_INET6)
+		return EINVAL;
+	
+	if (dgram->size < sizeof(icmpv6_message_t) + sizeof(ndp_message_t))
+		return EINVAL;
+	
+	icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data;
+	
+	ndp->opcode = icmpv6->type;
+	
+	ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1);
+	
+	addr128_t_be2host(message->target_address, ndp->target_proto_addr);
+	addr48(message->mac, ndp->sender_hw_addr);
+	
+	return EOK;
 }
 
Index: uspace/srv/net/inetsrv/pdu.h
===================================================================
--- uspace/srv/net/inetsrv/pdu.h	(revision 08ed1377c1cf36aaa5a0cc9311ce8b841365d06e)
+++ uspace/srv/net/inetsrv/pdu.h	(revision a17356fd13769fdbaa2f4df26127bb541d940247)
@@ -40,4 +40,5 @@
 #include <sys/types.h>
 #include "inetsrv.h"
+#include "ndp.h"
 
 #define INET_CHECKSUM_INIT 0xffff
@@ -45,8 +46,13 @@
 extern uint16_t inet_checksum_calc(uint16_t, void *, size_t);
 
-extern int inet_pdu_encode(inet_packet_t *, size_t, size_t, void **,
-    size_t *, size_t *);
+extern int inet_pdu_encode(inet_packet_t *, addr32_t, addr32_t, size_t, size_t,
+    void **, size_t *, size_t *);
+extern int inet_pdu_encode6(inet_packet_t *, addr128_t, addr128_t, size_t,
+    size_t, void **, size_t *, size_t *);
 extern int inet_pdu_decode(void *, size_t, inet_packet_t *);
 extern int inet_pdu_decode6(void *, size_t, inet_packet_t *);
+
+extern int ndp_pdu_decode(inet_dgram_t *, ndp_packet_t *);
+extern int ndp_pdu_encode(ndp_packet_t *, inet_dgram_t *);
 
 #endif
