Index: uspace/srv/net/udp/assoc.c
===================================================================
--- uspace/srv/net/udp/assoc.c	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/assoc.c	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -40,5 +40,4 @@
 #include <fibril_synch.h>
 #include <inet/endpoint.h>
-#include <inet/inet.h>
 #include <io/log.h>
 #include <nettl/amap.h>
@@ -48,5 +47,4 @@
 #include "msg.h"
 #include "pdu.h"
-#include "udp_inet.h"
 #include "udp_type.h"
 
@@ -57,7 +55,8 @@
 static udp_assoc_t *udp_assoc_find_ref(inet_ep2_t *);
 static errno_t udp_assoc_queue_msg(udp_assoc_t *, inet_ep2_t *, udp_msg_t *);
+static udp_assocs_dep_t *assocs_dep;
 
 /** Initialize associations. */
-errno_t udp_assocs_init(void)
+errno_t udp_assocs_init(udp_assocs_dep_t *dep)
 {
 	errno_t rc;
@@ -69,4 +68,5 @@
 	}
 
+	assocs_dep = dep;
 	return EOK;
 }
@@ -253,5 +253,4 @@
 errno_t udp_assoc_send(udp_assoc_t *assoc, inet_ep_t *remote, udp_msg_t *msg)
 {
-	udp_pdu_t *pdu;
 	inet_ep2_t epp;
 	errno_t rc;
@@ -275,5 +274,6 @@
 	if (inet_addr_is_any(&epp.local.addr) && !assoc->nolocal) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "Determine local address.");
-		rc = inet_get_srcaddr(&epp.remote.addr, 0, &epp.local.addr);
+		rc = (*assocs_dep->get_srcaddr)(&epp.remote.addr, 0,
+		    &epp.local.addr);
 		if (rc != EOK) {
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "Cannot determine "
@@ -289,14 +289,6 @@
 		return EINVAL;
 
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - encode pdu");
-
-	rc = udp_pdu_encode(&epp, msg, &pdu);
-	if (rc != EOK)
-		return ENOMEM;
-
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - transmit");
-
-	rc = udp_transmit_pdu(pdu);
-	udp_pdu_delete(pdu);
+	rc = (*assocs_dep->transmit_msg)(&epp, msg);
 
 	if (rc != EOK)
Index: uspace/srv/net/udp/assoc.h
===================================================================
--- uspace/srv/net/udp/assoc.h	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/assoc.h	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -40,5 +40,5 @@
 #include "udp_type.h"
 
-extern errno_t udp_assocs_init(void);
+extern errno_t udp_assocs_init(udp_assocs_dep_t *);
 extern void udp_assocs_fini(void);
 extern udp_assoc_t *udp_assoc_new(inet_ep2_t *, udp_assoc_cb_t *, void *);
Index: uspace/srv/net/udp/meson.build
===================================================================
--- uspace/srv/net/udp/meson.build	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/meson.build	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -32,8 +32,8 @@
 	'assoc.c',
 	'msg.c',
+	'pdu.c',
 )
 
 src = files(
-	'pdu.c',
 	'service.c',
 	'udp.c',
Index: uspace/srv/net/udp/test/assoc.c
===================================================================
--- uspace/srv/net/udp/test/assoc.c	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/test/assoc.c	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -46,4 +46,15 @@
 };
 
+static errno_t test_get_srcaddr(inet_addr_t *, uint8_t, inet_addr_t *);
+static errno_t test_transmit_msg(inet_ep2_t *, udp_msg_t *);
+
+static udp_assocs_dep_t test_assocs_dep = {
+	.get_srcaddr = test_get_srcaddr,
+	.transmit_msg = test_transmit_msg
+};
+
+static inet_ep2_t *sent_epp;
+static udp_msg_t *sent_msg;
+
 PCUT_TEST_BEFORE
 {
@@ -54,5 +65,5 @@
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 
-	rc = udp_assocs_init();
+	rc = udp_assocs_init(&test_assocs_dep);
 	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
 }
@@ -129,6 +140,199 @@
  }
 
-PCUT_TEST(send_recv)
-{
+/** Sending message with destination not set in association and NULL
+ * destination argument should fail with EINVAL.
+ */
+PCUT_TEST(send_null_dest)
+{
+	udp_assoc_t *assoc;
+	inet_ep2_t epp;
+	inet_ep_t dest;
+	errno_t rc;
+	udp_msg_t *msg;
+	const char *msgstr = "Hello";
+
+	msg = udp_msg_new();
+	PCUT_ASSERT_NOT_NULL(msg);
+	msg->data_size = str_size(msgstr) + 1;
+	msg->data = str_dup(msgstr);
+
+	inet_ep2_init(&epp);
+	inet_ep_init(&dest);
+
+	assoc = udp_assoc_new(&epp, &test_assoc_cb, NULL);
+	PCUT_ASSERT_NOT_NULL(assoc);
+
+	rc = udp_assoc_add(assoc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = udp_assoc_send(assoc, &dest, msg);
+	PCUT_ASSERT_ERRNO_VAL(EINVAL, rc);
+
+	udp_msg_delete(msg);
+
+	udp_assoc_remove(assoc);
+	udp_assoc_delete(assoc);
+}
+
+/** Sending message with destination not set in association and unset
+ * destination argument should fail with EINVAL.
+ */
+PCUT_TEST(send_unset_dest)
+{
+	udp_assoc_t *assoc;
+	inet_ep2_t epp;
+	inet_ep_t dest;
+	errno_t rc;
+	udp_msg_t *msg;
+	const char *msgstr = "Hello";
+
+	msg = udp_msg_new();
+	PCUT_ASSERT_NOT_NULL(msg);
+	msg->data_size = str_size(msgstr) + 1;
+	msg->data = str_dup(msgstr);
+
+	inet_ep2_init(&epp);
+	inet_ep_init(&dest);
+
+	assoc = udp_assoc_new(&epp, &test_assoc_cb, NULL);
+	PCUT_ASSERT_NOT_NULL(assoc);
+
+	rc = udp_assoc_add(assoc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = udp_assoc_send(assoc, &dest, msg);
+	PCUT_ASSERT_ERRNO_VAL(EINVAL, rc);
+
+	udp_msg_delete(msg);
+
+	udp_assoc_remove(assoc);
+	udp_assoc_delete(assoc);
+}
+
+/** Sending message with explicit destination */
+PCUT_TEST(send_explicit_dest)
+{
+	udp_assoc_t *assoc;
+	inet_ep2_t epp;
+	inet_ep_t dest;
+	errno_t rc;
+	udp_msg_t *msg;
+	const char *msgstr = "Hello";
+
+	msg = udp_msg_new();
+	PCUT_ASSERT_NOT_NULL(msg);
+	msg->data_size = str_size(msgstr) + 1;
+	msg->data = str_dup(msgstr);
+
+	inet_ep2_init(&epp);
+	inet_addr(&dest.addr, 127, 0, 0, 1);
+	dest.port = 42;
+
+	assoc = udp_assoc_new(&epp, &test_assoc_cb, NULL);
+	PCUT_ASSERT_NOT_NULL(assoc);
+
+	rc = udp_assoc_add(assoc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sent_epp = NULL;
+	sent_msg = NULL;
+
+	rc = udp_assoc_send(assoc, &dest, msg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(msg, sent_msg);
+	PCUT_ASSERT_TRUE(inet_addr_compare(&dest.addr, &sent_epp->remote.addr));
+	PCUT_ASSERT_EQUALS(dest.port, sent_epp->remote.port);
+
+	udp_msg_delete(msg);
+
+	udp_assoc_remove(assoc);
+	udp_assoc_delete(assoc);
+}
+
+/** Sending message with destination set in association and NULL destination
+ * argument
+ */
+PCUT_TEST(send_assoc_null_dest)
+{
+	udp_assoc_t *assoc;
+	inet_ep2_t epp;
+	errno_t rc;
+	udp_msg_t *msg;
+	const char *msgstr = "Hello";
+
+	msg = udp_msg_new();
+	PCUT_ASSERT_NOT_NULL(msg);
+	msg->data_size = str_size(msgstr) + 1;
+	msg->data = str_dup(msgstr);
+
+	inet_ep2_init(&epp);
+	inet_addr(&epp.remote.addr, 127, 0, 0, 1);
+	epp.remote.port = 42;
+	inet_addr(&epp.local.addr, 127, 0, 0, 1);
+	epp.local.port = 1;
+
+	assoc = udp_assoc_new(&epp, &test_assoc_cb, NULL);
+	PCUT_ASSERT_NOT_NULL(assoc);
+
+	rc = udp_assoc_add(assoc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	sent_epp = NULL;
+	sent_msg = NULL;
+
+	rc = udp_assoc_send(assoc, NULL, msg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(msg, sent_msg);
+	PCUT_ASSERT_TRUE(inet_addr_compare(&epp.remote.addr, &sent_epp->remote.addr));
+	PCUT_ASSERT_EQUALS(epp.remote.port, sent_epp->remote.port);
+
+	udp_msg_delete(msg);
+
+	udp_assoc_remove(assoc);
+	udp_assoc_delete(assoc);
+}
+
+/** Sending message with destination set in association and unset destination
+ * argument should return EINVAL
+ */
+PCUT_TEST(send_assoc_unset_dest)
+{
+	udp_assoc_t *assoc;
+	inet_ep2_t epp;
+	inet_ep_t dest;
+	errno_t rc;
+	udp_msg_t *msg;
+	const char *msgstr = "Hello";
+
+	msg = udp_msg_new();
+	PCUT_ASSERT_NOT_NULL(msg);
+	msg->data_size = str_size(msgstr) + 1;
+	msg->data = str_dup(msgstr);
+
+	inet_ep2_init(&epp);
+	inet_addr(&epp.remote.addr, 127, 0, 0, 1);
+	epp.remote.port = 42;
+	inet_addr(&epp.local.addr, 127, 0, 0, 1);
+	epp.local.port = 1;
+	inet_ep_init(&dest);
+
+	assoc = udp_assoc_new(&epp, &test_assoc_cb, NULL);
+	PCUT_ASSERT_NOT_NULL(assoc);
+
+	rc = udp_assoc_add(assoc);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = udp_assoc_send(assoc, &dest, msg);
+	PCUT_ASSERT_ERRNO_VAL(EINVAL, rc);
+
+	udp_msg_delete(msg);
+
+	udp_assoc_remove(assoc);
+	udp_assoc_delete(assoc);
+}
+
+PCUT_TEST(recv)
+{
+	// XXX Looks like currently udp_assoc_recv() is not used at all
 }
 
@@ -189,3 +393,17 @@
 }
 
+static errno_t test_get_srcaddr(inet_addr_t *remote, uint8_t tos,
+    inet_addr_t *local)
+{
+	inet_addr(local, 127, 0, 0, 1);
+	return EOK;
+}
+
+static errno_t test_transmit_msg(inet_ep2_t *epp, udp_msg_t *msg)
+{
+	sent_epp = epp;
+	sent_msg = msg;
+	return EOK;
+}
+
 PCUT_EXPORT(assoc);
Index: uspace/srv/net/udp/udp.c
===================================================================
--- uspace/srv/net/udp/udp.c	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/udp.c	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -47,4 +47,9 @@
 #define NAME       "udp"
 
+static udp_assocs_dep_t udp_assocs_dep = {
+	.get_srcaddr = udp_get_srcaddr,
+	.transmit_msg = udp_transmit_msg
+};
+
 static errno_t udp_init(void)
 {
@@ -53,5 +58,5 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_init()");
 
-	rc = udp_assocs_init();
+	rc = udp_assocs_init(&udp_assocs_dep);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing associations.");
Index: uspace/srv/net/udp/udp_inet.c
===================================================================
--- uspace/srv/net/udp/udp_inet.c	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/udp_inet.c	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -134,4 +134,36 @@
 }
 
+/** Get source address.
+ *
+ * @param remote Remote address
+ * @param tos Type of service
+ * @param local Place to store local address
+ * @return EOK on success or an error code
+ */
+errno_t udp_get_srcaddr(inet_addr_t *remote, uint8_t tos, inet_addr_t *local)
+{
+	return inet_get_srcaddr(remote, tos, local);
+}
+
+/** Transmit message over network layer. */
+errno_t udp_transmit_msg(inet_ep2_t *epp, udp_msg_t *msg)
+{
+	udp_pdu_t *pdu;
+	errno_t rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_transmit_msg()");
+
+	rc = udp_pdu_encode(epp, msg, &pdu);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed encoding PDU");
+		return rc;
+	}
+
+	rc = udp_transmit_pdu(pdu);
+	udp_pdu_delete(pdu);
+
+	return rc;
+}
+
 /**
  * @}
Index: uspace/srv/net/udp/udp_inet.h
===================================================================
--- uspace/srv/net/udp/udp_inet.h	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/udp_inet.h	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -36,8 +36,12 @@
 #define UDP_INET_H
 
+#include <inet/addr.h>
+#include <stdint.h>
 #include "udp_type.h"
 
 extern errno_t udp_inet_init(void);
 extern errno_t udp_transmit_pdu(udp_pdu_t *);
+extern errno_t udp_get_srcaddr(inet_addr_t *, uint8_t, inet_addr_t *);
+extern errno_t udp_transmit_msg(inet_ep2_t *, udp_msg_t *);
 
 #endif
Index: uspace/srv/net/udp/udp_type.h
===================================================================
--- uspace/srv/net/udp/udp_type.h	(revision 255251333a4297ae92eaac46deaff207b5dcca67)
+++ uspace/srv/net/udp/udp_type.h	(revision 89ba88c9450661ceee63cfe6f84a5d96f9ec0f4f)
@@ -37,4 +37,5 @@
 
 #include <async.h>
+#include <errno.h>
 #include <fibril.h>
 #include <fibril_synch.h>
@@ -44,4 +45,5 @@
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <inet/addr.h>
 
@@ -87,5 +89,19 @@
 } udp_pdu_t;
 
-/** Association callbacks */
+/** Functions needed by associations module.
+ *
+ * Functions that need to be provided by the caller so that the associations
+ * module can function.
+ */
+typedef struct {
+	errno_t (*get_srcaddr)(inet_addr_t *, uint8_t, inet_addr_t *);
+	errno_t (*transmit_msg)(inet_ep2_t *, udp_msg_t *);
+} udp_assocs_dep_t;
+
+/** Association callbacks.
+ *
+ * Callbacks for a particular association, to notify caller of events
+ * on the association.
+ */
 typedef struct {
 	/** Message received */
