Index: uspace/app/inetcfg/inetcfg.c
===================================================================
--- uspace/app/inetcfg/inetcfg.c	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/app/inetcfg/inetcfg.c	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -373,6 +373,6 @@
 		}
 
-		printf("    %s %s %s\n", astr, linfo.name,
-		    ainfo.name);
+		printf("    %s %s %s %zu\n", astr, linfo.name,
+		    ainfo.name, linfo.def_mtu);
 
 		free(ainfo.name);
Index: uspace/lib/c/generic/inetcfg.c
===================================================================
--- uspace/lib/c/generic/inetcfg.c	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/lib/c/generic/inetcfg.c	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -276,4 +276,5 @@
 
 	linfo->name = str_dup(name_buf);
+	linfo->def_mtu = IPC_GET_ARG1(answer);
 
 	return EOK;
Index: uspace/lib/c/include/inet/inetcfg.h
===================================================================
--- uspace/lib/c/include/inet/inetcfg.h	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/lib/c/include/inet/inetcfg.h	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -61,4 +61,6 @@
 	/** Link service name */
 	char *name;
+	/** Default MTU */
+	size_t def_mtu;
 } inet_link_info_t;
 
Index: uspace/srv/inet/inet.h
===================================================================
--- uspace/srv/inet/inet.h	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/srv/inet/inet.h	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -88,4 +88,6 @@
 	/** Link service name */
 	char *name;
+	/** Default MTU */
+	size_t def_mtu;
 } inet_link_info_t;
 
@@ -125,4 +127,5 @@
 	async_sess_t *sess;
 	iplink_t *iplink;
+	size_t def_mtu;
 } inet_link_t;
 
Index: uspace/srv/inet/inet_link.c
===================================================================
--- uspace/srv/inet/inet_link.c	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/srv/inet/inet_link.c	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -161,4 +161,5 @@
 
 	ilink->svc_id = sid;
+	ilink->iplink = NULL;
 
 	rc = loc_service_get_name(sid, &ilink->svc_name);
@@ -177,4 +178,11 @@
 	if (rc != EOK) {
 		log_msg(LVL_ERROR, "Failed opening IP link '%s'",
+		    ilink->svc_name);
+		goto error;
+	}
+
+	rc = iplink_get_mtu(ilink->iplink, &ilink->def_mtu);
+	if (rc != EOK) {
+		log_msg(LVL_ERROR, "Failed determinning MTU of link '%s'",
 		    ilink->svc_name);
 		goto error;
@@ -211,4 +219,6 @@
 
 error:
+	if (ilink->iplink != NULL)
+		iplink_close(ilink->iplink);
 	inet_link_delete(ilink);
 	return rc;
@@ -241,6 +251,10 @@
 	inet_packet_t packet;
 	int rc;
-
-	/* XXX Fragment packet */
+	size_t offs, roffs;
+
+	/*
+	 * Fill packet structure. Fragmentation is performed by
+	 * inet_pdu_encode().
+	 */
 	packet.src = dgram->src;
 	packet.dest = dgram->dest;
@@ -254,10 +268,19 @@
 	sdu.lsrc.ipv4 = lsrc->ipv4;
 	sdu.ldest.ipv4 = ldest->ipv4;
-	rc = inet_pdu_encode(&packet, &sdu.data, &sdu.size);
-	if (rc != EOK)
-		return rc;
-
-	rc = iplink_send(ilink->iplink, &sdu);
-	free(sdu.data);
+
+	offs = 0;
+	do {
+		/* Encode one fragment */
+		rc = inet_pdu_encode(&packet, offs, ilink->def_mtu, &sdu.data,
+		    &sdu.size, &roffs);
+		if (rc != EOK)
+			return rc;
+
+		/* Send the PDU */
+		rc = iplink_send(ilink->iplink, &sdu);
+		free(sdu.data);
+
+		offs = roffs;
+	} while (offs < packet.size);
 
 	return rc;
Index: uspace/srv/inet/inet_std.h
===================================================================
--- uspace/srv/inet/inet_std.h	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/srv/inet/inet_std.h	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -90,4 +90,7 @@
 };
 
+/** Fragment offset is expressed in units of 8 bytes */
+#define FRAG_OFFS_UNIT 8
+
 #endif
 
Index: uspace/srv/inet/inetcfg.c
===================================================================
--- uspace/srv/inet/inetcfg.c	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/srv/inet/inetcfg.c	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -164,4 +164,5 @@
 
 	linfo->name = str_dup(ilink->svc_name);
+	linfo->def_mtu = ilink->def_mtu;
 	return EOK;
 }
@@ -464,5 +465,5 @@
 	free(linfo.name);
 
-	async_answer_0(callid, retval);
+	async_answer_1(callid, retval, linfo.def_mtu);
 }
 
Index: uspace/srv/inet/pdu.c
===================================================================
--- uspace/srv/inet/pdu.c	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/srv/inet/pdu.c	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -41,4 +41,5 @@
 #include <fibril_synch.h>
 #include <io/log.h>
+#include <macros.h>
 #include <mem.h>
 #include <stdlib.h>
@@ -88,6 +89,20 @@
 
 /** Encode Internet PDU.
- */
-int inet_pdu_encode(inet_packet_t *packet, void **rdata, size_t *rsize)
+ *
+ * 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 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)
 {
 	void *data;
@@ -98,17 +113,59 @@
 	uint16_t chksum;
 	uint16_t ident;
-
+	uint16_t flags_foff;
+	uint16_t foff;
+	size_t fragoff_limit;
+	size_t xfer_size;
+	size_t spc_avail;
+	size_t rem_offs;
+
+	/* Upper bound for fragment offset field */
+	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;
+
+	hdr_size = sizeof(ip_header_t);
+	data_offs = ROUND_UP(hdr_size, 4);
+
+	assert(offs % FRAG_OFFS_UNIT == 0);
+	assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
+
+	/* Value for the fragment offset field */
+	foff = offs / FRAG_OFFS_UNIT;
+
+	if (hdr_size >= mtu)
+		return EINVAL;
+
+	/* Amount of space in the PDU available for payload */
+	spc_avail = mtu - hdr_size;
+
+	/* Amount of data (payload) to transfer */
+	xfer_size = min(packet->size - offs, spc_avail);
+	xfer_size -= (xfer_size % FRAG_OFFS_UNIT);
+
+	/* Total PDU size */
+	size = hdr_size + xfer_size;
+
+	/* Offset of remaining payload */
+	rem_offs = offs + xfer_size;
+
+	/* Flags */
+	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);
+
+	data = calloc(size, 1);
+	if (data == NULL)
+		return ENOMEM;
+
+	/* Allocate identifier */
 	fibril_mutex_lock(&ip_ident_lock);
 	ident = ++ip_ident;
 	fibril_mutex_unlock(&ip_ident_lock);
 
-	hdr_size = sizeof(ip_header_t);
-	size = hdr_size + packet->size;
-	data_offs = ROUND_UP(hdr_size, 4);
-
-	data = calloc(size, 1);
-	if (data == NULL)
-		return ENOMEM;
-
+	/* Encode header fields */
 	hdr = (ip_header_t *)data;
 	hdr->ver_ihl = (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
@@ -116,6 +173,5 @@
 	hdr->tot_len = host2uint16_t_be(size);
 	hdr->id = host2uint16_t_be(ident);
-	hdr->flags_foff = host2uint16_t_be(packet->df ?
-	    BIT_V(uint16_t, FF_FLAG_DF) : 0);
+	hdr->flags_foff = host2uint16_t_be(flags_foff);
 	hdr->ttl = packet->ttl;
 	hdr->proto = packet->proto;
@@ -124,11 +180,15 @@
 	hdr->dest_addr = host2uint32_t_be(packet->dest.ipv4);
 
+	/* Compute checksum */
 	chksum = inet_checksum_calc(INET_CHECKSUM_INIT, (void *)hdr, hdr_size);
 	hdr->chksum = host2uint16_t_be(chksum);
 
-	memcpy((uint8_t *)data + data_offs, packet->data, packet->size);
+	/* Copy payload */
+	memcpy((uint8_t *)data + data_offs, packet->data + offs, xfer_size);
 
 	*rdata = data;
 	*rsize = size;
+	*roffs = rem_offs;
+
 	return EOK;
 }
Index: uspace/srv/inet/pdu.h
===================================================================
--- uspace/srv/inet/pdu.h	(revision 7094e19603eaaf77ac66a4de4e0515e8763a5d55)
+++ uspace/srv/inet/pdu.h	(revision 347768dbcad36451941a7a273c43bf6457da911b)
@@ -45,5 +45,6 @@
 extern uint16_t inet_checksum_calc(uint16_t, void *, size_t);
 
-extern int inet_pdu_encode(inet_packet_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_decode(void *, size_t, inet_packet_t *);
 
