diff --git a/openbsc/include/openbsc/md5.h b/openbsc/include/openbsc/md5.h
new file mode 100644
index 0000000..938c257
--- /dev/null
+++ b/openbsc/include/openbsc/md5.h
@@ -0,0 +1,59 @@
+/*
+ * MD5.H - header file for MD5C.C
+ */
+
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
+ * reserved.
+ * 
+ * License to copy and use this software is granted provided that it is
+ * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm"
+ * in all material mentioning or referencing this software or this function.
+ * 
+ * License is also granted to make and use derivative works provided that such
+ * works are identified as "derived from the RSA Data Security, Inc. MD5
+ * Message-Digest Algorithm" in all material mentioning or referencing the
+ * derived work.
+ * 
+ * RSA Data Security, Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ * 
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#ifndef _MD5_H
+#define _MD5_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+/* MD5 context. */
+typedef struct {
+        UINT4           state[4];       /* state (ABCD) */
+        UINT4           count[2];       /* number of bits, modulo 2^64 (lsb
+                                         * first) */
+        unsigned char   buffer[64];     /* input buffer */
+}               MD5_CTX;
+
+void MD5Init    (MD5_CTX *);
+void MD5Update  (MD5_CTX *, unsigned char *, unsigned int);
+void MD5Final   (unsigned char[16], MD5_CTX *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* md5.h */
diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h
index 68d76ab..4b749e8 100644
--- a/openbsc/include/openbsc/mncc.h
+++ b/openbsc/include/openbsc/mncc.h
@@ -87,7 +87,7 @@ struct gsm_call {
 #define MNCC_FRAME_DROP		0x0202
 #define MNCC_LCHAN_MODIFY	0x0203
 
-#define GSM_TRAU_FRAME		0x0300
+#define GSM_TCHF_FRAME		0x0300
 
 #define GSM_MAX_FACILITY	128
 #define GSM_MAX_SSVERSION	128
@@ -199,7 +199,7 @@ struct gsm_mncc {
 	unsigned char	lchan_mode;
 };
 
-struct gsm_trau_frame {
+struct gsm_data_frame {
 	u_int32_t	msg_type;
 	u_int32_t	callref;
 	unsigned char	data[0];
diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h
index e9fc157..a3e4596 100644
--- a/openbsc/include/openbsc/rtp_proxy.h
+++ b/openbsc/include/openbsc/rtp_proxy.h
@@ -34,6 +34,11 @@ enum rtp_rx_action {
 	RTP_RECV_UPSTREAM,
 };
 
+enum rtp_tx_action {
+	RTP_SEND_NONE,
+	RTP_SEND_DOWNSTREAM,
+};
+
 struct rtp_sub_socket {
 	struct sockaddr_in sin_local;
 	struct sockaddr_in sin_remote;
@@ -56,15 +61,24 @@ struct rtp_socket {
 			struct rtp_socket *other_sock;
 		} proxy;
 		struct {
-			void (*recv_cb)(struct msgb *msg);
+			struct gsm_network *net;
+			u_int32_t callref;
 		} receive;
 	};
+	enum rtp_tx_action tx_action;
+	struct {
+		u_int16_t sequence;
+		u_int32_t timestamp;
+		u_int32_t ssrc;
+	} transmit;
 };
 
 struct rtp_socket *rtp_socket_create(void);
 int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip);
 int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port);
 int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other);
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, u_int32_t callref);
 int rtp_socket_free(struct rtp_socket *rs);
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame);
 
 #endif /* _RTP_PROXY_H */
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
index 961a649..6ffe254 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -26,6 +26,9 @@ struct gsm_trans {
 	/* reference from MNCC or other application */
 	u_int32_t callref;
 
+	/* if traffic channel receive was requested */
+	int tch_recv;
+
 	union {
 		struct {
 
diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h
index 90535ad..8deb708 100644
--- a/openbsc/include/openbsc/trau_mux.h
+++ b/openbsc/include/openbsc/trau_mux.h
@@ -46,4 +46,4 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
 int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref);
 
 /* send trau from application */
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf);
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame);
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index 919e075..726c712 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -13,7 +13,7 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
 		talloc_ctx.c
 
 libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
-		mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
+		mncc.c rtp_proxy.c md5c.c gsm_04_08.c gsm_04_11.c transaction.c \
 		token_auth.c rrlp.c
 
 libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index bac920e..f960d17 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -1869,6 +1869,8 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
 	return 0;
 }
 
+static int tch_recv(struct gsm_network *net, u_int32_t callref, int enable);
+
 /* some other part of the code sends us a signal */
 static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
 				 void *handler_data, void *signal_data)
@@ -1876,6 +1878,8 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
 	struct gsm_lchan *lchan = signal_data;
 	struct gsm_bts_trx_ts *ts;
 	int rc;
+	struct gsm_network *net;
+	struct gsm_trans *trans;
 
 	if (subsys != SS_ABISIP)
 		return 0;
@@ -1904,6 +1908,14 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
 				   ts->abis_ip.bound_port);
 		if (rc < 0)
 			goto out_err;
+		/* receive if whish is pending */
+		net = ts->trx->bts->network;
+		llist_for_each_entry(trans, &net->trans_list, entry) {
+			if (trans->lchan == lchan && trans->tch_recv) {
+				DEBUGP(DCC, "is pending tch_recv whish\n");
+				tch_recv(net, trans->callref, 1);
+			}
+		}
 		break;
 	case S_ABISIP_DISC_IND:
 		/* the BTS tells us a RTP stream has been disconnected */
@@ -1952,7 +1964,8 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
 		DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n");
 		return -EINVAL;
 	}
-	
+
+	// todo: map between different bts types
 	switch (bts->type) {
 	case GSM_BTS_TYPE_NANOBTS_900:
 	case GSM_BTS_TYPE_NANOBTS_1800:
@@ -1962,6 +1975,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
 			if (rc < 0)
 				return rc;
 			rc = ipacc_connect_proxy_bind(remote_lchan);
+#warning do we need a check of rc here?
 
 			/* connect them with each other */
 			rtp_socket_proxy(lchan->ts->abis_ip.rtp_socket,
@@ -1987,8 +2001,7 @@ static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
 		break;
 	default:
 		DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
-		rc = -EINVAL;
-		break;
+		return -EINVAL;
 	}
 
 	return 0;
@@ -2011,44 +2024,60 @@ static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
 }
 
 /* enable receive of channels to upqueue */
-static int tch_recv(struct gsm_network *net, struct gsm_mncc *data, int enable)
+static int tch_recv(struct gsm_network *net, u_int32_t callref, int enable)
 {
 	struct gsm_trans *trans;
+	struct gsm_lchan *lchan;
+	struct gsm_bts *bts;
+	int rc;
 
 	/* Find callref */
-	trans = trans_find_by_callref(net, data->callref);
+	trans = trans_find_by_callref(net, callref);
 	if (!trans)
 		return -EIO;
 	if (!trans->lchan)
 		return 0;
+	lchan = trans->lchan;
+	bts = lchan->ts->trx->bts;
 
-	// todo IPACCESS
-	if (enable)
-		return trau_recv_lchan(trans->lchan, data->callref);
-	return trau_mux_unmap(NULL, data->callref);
-}
-
-/* send a frame to channel */
-static int tch_frame(struct gsm_network *net, struct gsm_trau_frame *frame)
-{
-	struct gsm_trans *trans;
-
-	/* Find callref */
-	trans = trans_find_by_callref(net, frame->callref);
-	if (!trans)
-		return -EIO;
-	if (!trans->lchan)
-		return 0;
-	if (trans->lchan->type != GSM_LCHAN_TCH_F &&
-	    trans->lchan->type != GSM_LCHAN_TCH_H)
-		return 0;
+	switch (bts->type) {
+	case GSM_BTS_TYPE_NANOBTS_900:
+	case GSM_BTS_TYPE_NANOBTS_1800:
+		if (ipacc_rtp_direct) {
+			DEBUGP(DCC, "Error: RTP proxy is disabled\n");
+			return -EINVAL;
+		}
+		/* in case, we don't have a socket yet */
+		if (!lchan->ts->abis_ip.rtp_socket) {
+			trans->tch_recv = enable;
+			DEBUGP(DCC, "queue tch_recv whish (%d)\n", enable);
+			return 0;
+		}
+		if (enable) {
+			/* connect the TCH's to our RTP proxy */
+			rc = ipacc_connect_proxy_bind(lchan);
+			if (rc < 0)
+				return rc;
+			/* assign socket to application interface */
+			rtp_socket_upstream(lchan->ts->abis_ip.rtp_socket,
+				net, callref);
+		} else
+			rtp_socket_upstream(lchan->ts->abis_ip.rtp_socket,
+				net, 0);
+		break;
+	case GSM_BTS_TYPE_BS11:
+		if (enable)
+			return trau_recv_lchan(lchan, callref);
+		return trau_mux_unmap(NULL, callref);
+		break;
+	default:
+		DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+		return -EINVAL;
+	}
 
-	// todo IPACCESS
-	return trau_send_lchan(trans->lchan, 
-				(struct decoded_trau_frame *)frame->data);
+	return 0;
 }
 
-
 static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg)
 {
 	DEBUGP(DCC, "-> STATUS ENQ\n");
@@ -3287,11 +3316,31 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
 	case MNCC_BRIDGE:
 		return tch_bridge(net, arg);
 	case MNCC_FRAME_DROP:
-		return tch_recv(net, arg, 0);
+		return tch_recv(net, data->callref, 0);
 	case MNCC_FRAME_RECV:
-		return tch_recv(net, arg, 1);
-	case GSM_TRAU_FRAME:
-		return tch_frame(net, arg);
+		return tch_recv(net, data->callref, 1);
+	case GSM_TCHF_FRAME:
+		/* Find callref */
+		trans = trans_find_by_callref(net, data->callref);
+		if (!trans)
+			return -EIO;
+		if (!trans->lchan)
+			return 0;
+		if (trans->lchan->type != GSM_LCHAN_TCH_F)
+			return 0;
+		bts = trans->lchan->ts->trx->bts;
+		switch (bts->type) {
+		case GSM_BTS_TYPE_NANOBTS_900:
+		case GSM_BTS_TYPE_NANOBTS_1800:
+			if (!trans->lchan->ts->abis_ip.rtp_socket)
+				return 0;
+			return rtp_send_frame(trans->lchan->ts->abis_ip.rtp_socket, arg);
+		case GSM_BTS_TYPE_BS11:
+			return trau_send_frame(trans->lchan, arg);
+		default:
+			DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+		}
+		return -EINVAL;
 	}
 
 	memset(&rel, 0, sizeof(struct gsm_mncc));
diff --git a/openbsc/src/md5c.c b/openbsc/src/md5c.c
new file mode 100644
index 0000000..4b1d15e
--- /dev/null
+++ b/openbsc/src/md5c.c
@@ -0,0 +1,323 @@
+/*
+ * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/*
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
+ * reserved.
+ * 
+ * License to copy and use this software is granted provided that it is
+ * identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm"
+ * in all material mentioning or referencing this software or this function.
+ * 
+ * License is also granted to make and use derivative works provided that such
+ * works are identified as "derived from the RSA Data Security, Inc. MD5
+ * Message-Digest Algorithm" in all material mentioning or referencing the
+ * derived work.
+ * 
+ * RSA Data Security, Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ * 
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ */
+
+#include "openbsc/md5.h"
+
+#include <string.h>
+
+/*
+ * Constants for MD5Transform routine.
+ */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform (UINT4[4], unsigned char[64]);
+static void Encode (unsigned char *, UINT4 *, unsigned int);
+static void Decode (UINT4 *, unsigned char *, unsigned int);
+
+static unsigned char PADDING[64] = {
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/*
+ * ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is
+ * separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+  }
+
+/*
+ * MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void 
+MD5Init( MD5_CTX        *context)/* context */
+{
+        context->count[0] = context->count[1] = 0;
+        /*
+         * Load magic initialization constants.
+         */
+        context->state[0] = 0x67452301;
+        context->state[1] = 0xefcdab89;
+        context->state[2] = 0x98badcfe;
+        context->state[3] = 0x10325476;
+}
+
+/*
+ * MD5 block update operation. Continues an MD5 message-digest operation,
+ * processing another message block, and updating the context.
+ */
+void 
+MD5Update(
+        MD5_CTX        *context,/* context */
+        unsigned char  *input,  /* input block */
+        unsigned int    inputLen)       /* length of input block */
+{
+        unsigned int    i, index, partLen;
+
+        /* Compute number of bytes mod 64 */
+        index = (unsigned int) ((context->count[0] >> 3) & 0x3F);
+
+        /* Update number of bits */
+        if ((context->count[0] += ((UINT4) inputLen << 3))
+            < ((UINT4) inputLen << 3))
+                context->count[1]++;
+        context->count[1] += ((UINT4) inputLen >> 29);
+
+        partLen = 64 - index;
+
+        /*
+         * Transform as many times as possible.
+         */
+        if (inputLen >= partLen) {
+                memcpy
+                        ((POINTER) & context->buffer[index], (POINTER) input, partLen);
+                MD5Transform(context->state, context->buffer);
+
+                for (i = partLen; i + 63 < inputLen; i += 64)
+                        MD5Transform(context->state, &input[i]);
+
+                index = 0;
+        } else
+                i = 0;
+
+        /* Buffer remaining input */
+        memcpy
+                ((POINTER) & context->buffer[index], (POINTER) & input[i],
+                 inputLen - i);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation, writing the the
+ * message digest and zeroizing the context.
+ */
+void 
+MD5Final(
+        unsigned char   digest[16],     /* message digest */
+        MD5_CTX        *context)  /* context */
+{
+        unsigned char   bits[8];
+        unsigned int    index, padLen;
+
+        /* Save number of bits */
+        Encode(bits, context->count, 8);
+
+        /*
+         * Pad out to 56 mod 64.
+         */
+        index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
+        padLen = (index < 56) ? (56 - index) : (120 - index);
+        MD5Update(context, PADDING, padLen);
+
+        /* Append length (before padding) */
+        MD5Update(context, bits, 8);
+
+        /* Store state in digest */
+        Encode(digest, context->state, 16);
+
+        /*
+         * Zeroize sensitive information.
+         */
+        memset((POINTER) context, 0, sizeof(*context));
+}
+
+/*
+ * MD5 basic transformation. Transforms state based on block.
+ */
+static void 
+MD5Transform(
+        UINT4           state[4],
+        unsigned char   block[64])
+{
+        UINT4           a = state[0], b = state[1], c = state[2], d = state[3],
+                        x[16];
+
+        Decode(x, block, 64);
+
+        /* Round 1 */
+        FF(a, b, c, d, x[0], S11, 0xd76aa478);  /* 1 */
+        FF(d, a, b, c, x[1], S12, 0xe8c7b756);  /* 2 */
+        FF(c, d, a, b, x[2], S13, 0x242070db);  /* 3 */
+        FF(b, c, d, a, x[3], S14, 0xc1bdceee);  /* 4 */
+        FF(a, b, c, d, x[4], S11, 0xf57c0faf);  /* 5 */
+        FF(d, a, b, c, x[5], S12, 0x4787c62a);  /* 6 */
+        FF(c, d, a, b, x[6], S13, 0xa8304613);  /* 7 */
+        FF(b, c, d, a, x[7], S14, 0xfd469501);  /* 8 */
+        FF(a, b, c, d, x[8], S11, 0x698098d8);  /* 9 */
+        FF(d, a, b, c, x[9], S12, 0x8b44f7af);  /* 10 */
+        FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+        FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+        FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+        FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+        FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+        FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+        /* Round 2 */
+        GG(a, b, c, d, x[1], S21, 0xf61e2562);  /* 17 */
+        GG(d, a, b, c, x[6], S22, 0xc040b340);  /* 18 */
+        GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+        GG(b, c, d, a, x[0], S24, 0xe9b6c7aa);  /* 20 */
+        GG(a, b, c, d, x[5], S21, 0xd62f105d);  /* 21 */
+        GG(d, a, b, c, x[10], S22, 0x2441453);  /* 22 */
+        GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+        GG(b, c, d, a, x[4], S24, 0xe7d3fbc8);  /* 24 */
+        GG(a, b, c, d, x[9], S21, 0x21e1cde6);  /* 25 */
+        GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+        GG(c, d, a, b, x[3], S23, 0xf4d50d87);  /* 27 */
+        GG(b, c, d, a, x[8], S24, 0x455a14ed);  /* 28 */
+        GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+        GG(d, a, b, c, x[2], S22, 0xfcefa3f8);  /* 30 */
+        GG(c, d, a, b, x[7], S23, 0x676f02d9);  /* 31 */
+        GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+        /* Round 3 */
+        HH(a, b, c, d, x[5], S31, 0xfffa3942);  /* 33 */
+        HH(d, a, b, c, x[8], S32, 0x8771f681);  /* 34 */
+        HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+        HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+        HH(a, b, c, d, x[1], S31, 0xa4beea44);  /* 37 */
+        HH(d, a, b, c, x[4], S32, 0x4bdecfa9);  /* 38 */
+        HH(c, d, a, b, x[7], S33, 0xf6bb4b60);  /* 39 */
+        HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+        HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+        HH(d, a, b, c, x[0], S32, 0xeaa127fa);  /* 42 */
+        HH(c, d, a, b, x[3], S33, 0xd4ef3085);  /* 43 */
+        HH(b, c, d, a, x[6], S34, 0x4881d05);   /* 44 */
+        HH(a, b, c, d, x[9], S31, 0xd9d4d039);  /* 45 */
+        HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+        HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+        HH(b, c, d, a, x[2], S34, 0xc4ac5665);  /* 48 */
+
+        /* Round 4 */
+        II(a, b, c, d, x[0], S41, 0xf4292244);  /* 49 */
+        II(d, a, b, c, x[7], S42, 0x432aff97);  /* 50 */
+        II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+        II(b, c, d, a, x[5], S44, 0xfc93a039);  /* 52 */
+        II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+        II(d, a, b, c, x[3], S42, 0x8f0ccc92);  /* 54 */
+        II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+        II(b, c, d, a, x[1], S44, 0x85845dd1);  /* 56 */
+        II(a, b, c, d, x[8], S41, 0x6fa87e4f);  /* 57 */
+        II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+        II(c, d, a, b, x[6], S43, 0xa3014314);  /* 59 */
+        II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+        II(a, b, c, d, x[4], S41, 0xf7537e82);  /* 61 */
+        II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+        II(c, d, a, b, x[2], S43, 0x2ad7d2bb);  /* 63 */
+        II(b, c, d, a, x[9], S44, 0xeb86d391);  /* 64 */
+
+        state[0] += a;
+        state[1] += b;
+        state[2] += c;
+        state[3] += d;
+
+        /*
+         * Zeroize sensitive information.
+         */
+        memset((POINTER) x, 0, sizeof(x));
+}
+
+/*
+ * Encodes input (UINT4) into output (unsigned char). Assumes len is a
+ * multiple of 4.
+ */
+static void 
+Encode( unsigned char  *output,
+        UINT4          *input,
+        unsigned int    len)
+{
+        unsigned int    i, j;
+
+        for (i = 0, j = 0; j < len; i++, j += 4) {
+                output[j] = (unsigned char) (input[i] & 0xff);
+                output[j + 1] = (unsigned char) ((input[i] >> 8) & 0xff);
+                output[j + 2] = (unsigned char) ((input[i] >> 16) & 0xff);
+                output[j + 3] = (unsigned char) ((input[i] >> 24) & 0xff);
+        }
+}
+
+/*
+ * Decodes input (unsigned char) into output (UINT4). Assumes len is a
+ * multiple of 4.
+ */
+static void 
+Decode( UINT4          *output,
+        unsigned char  *input,
+        unsigned int    len)
+{
+        unsigned int    i, j;
+
+        for (i = 0, j = 0; j < len; i++, j += 4)
+                output[i] = ((UINT4) input[j]) | (((UINT4) input[j + 1]) << 8) |
+                        (((UINT4) input[j + 2]) << 16) | (((UINT4) input[j + 3]) << 24);
+}
+
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
index 59c0735..b92526e 100644
--- a/openbsc/src/rtp_proxy.c
+++ b/openbsc/src/rtp_proxy.c
@@ -24,6 +24,11 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <sys/time.h>    /* gettimeofday() */
+#include <unistd.h>      /* get..() */
+#include <time.h>        /* clock() */
+#include <sys/utsname.h> /* uname() */
+#include "openbsc/md5.h" /* from RFC 1321 */
 
 #include <openbsc/talloc.h>
 #include <openbsc/gsm_data.h>
@@ -57,6 +62,220 @@ struct rtcp_hdr {
 	
 #define RTCP_IE_CNAME	1
 
+/* according to RFC 3550 */
+struct rtp_hdr {
+	u_int8_t  csrc_count:4,
+		  extension:1,
+		  padding:1,
+		  version:2;
+	u_int8_t  payload_type:7,
+		  marker:1;
+	u_int16_t sequence;
+	u_int32_t timestamp;
+	u_int32_t ssrc;
+} __attribute__((packed));
+
+struct rtp_x_hdr {
+	u_int16_t by_profile;
+	u_int16_t length;
+} __attribute__((packed));
+
+#define RTP_VERSION	2
+
+#define RTP_PT_GSM_FULL	3
+
+/*
+ * Generate a random 32-bit quantity. (Appendix A. 6)
+ */
+
+#define MD_CTX MD5_CTX
+#define MDInit MD5Init
+#define MDUpdate MD5Update
+#define MDFinal MD5Final
+
+static u_int32_t md_32(unsigned char *string, int length)
+{
+	MD_CTX context;
+	union {
+		char      c[16];
+		u_int32_t x[4];
+	} digest;
+	u_int32_t r;
+	int i;
+
+	MDInit (&context);
+	MDUpdate (&context, string, length);
+	MDFinal ((unsigned char *)&digest, &context);
+	r = 0;
+	for (i = 0; i < 3; i++) {
+		r ^= digest.x[i];
+	}
+	return r;
+}
+
+/*
+ * Return random unsigned 32-bit quantity.  Use 'type' argument if
+ * you need to generate several different values in close succession.
+ */
+static u_int32_t random32(int type)
+{
+	struct {
+		int       type;
+		struct    timeval tv;
+		clock_t   cpu;
+		pid_t     pid;
+		u_int32_t hid;
+		uid_t     uid;
+		gid_t     gid;
+		struct    utsname name;
+	} s;
+
+	gettimeofday(&s.tv, 0);
+	uname(&s.name);
+	s.type = type;
+	s.cpu  = clock();
+	s.pid  = getpid();
+	s.hid  = gethostid();
+	s.uid  = getuid();
+	s.gid  = getgid();
+	/* also: system uptime */
+
+	return md_32((unsigned char *)&s, sizeof(s));
+}
+
+/* decode an rtp frame and create a new buffer with payload */
+static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
+{
+	struct msgb *new_msg;
+	struct gsm_data_frame *frame;
+	struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
+	struct rtp_x_hdr *rtpxh;
+	u_int8_t *payload;
+	int payload_len;
+	int msg_type;
+	int x_len;
+
+	if (msg->len < 12) {
+		DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n", msg->len);
+		return -EINVAL;
+	}
+	if (rtph->version != RTP_VERSION) {
+		DEBUGPC(DMUX, "received RTP version %d not supported.\n", rtph->version);
+		return -EINVAL;
+	}
+	payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
+	payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
+	if (payload_len < 0) {
+		DEBUGPC(DMUX, "received RTP frame too short (len = %d, csrc count = %d)\n",
+			msg->len, rtph->csrc_count);
+		return -EINVAL;
+	}
+	if (rtph->extension) {
+		if (payload_len < sizeof(struct rtp_x_hdr)) {
+			DEBUGPC(DMUX, "received RTP frame too short for extension header\n");
+			return -EINVAL;
+		}
+		rtpxh = (struct rtp_x_hdr *)payload;
+		x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
+		payload += x_len;
+		payload_len -= x_len;
+		if (payload_len < 0) {
+			DEBUGPC(DMUX, "received RTP frame too short, extension header exceeds frame length\n");
+			return -EINVAL;
+		}
+	}
+	if (rtph->padding) {
+		if (payload_len < 0) {
+			DEBUGPC(DMUX, "received RTP frame too short for padding length\n");
+			return -EINVAL;
+		}
+		payload_len -= payload[payload_len - 1];
+		if (payload_len < 0) {
+			DEBUGPC(DMUX, "received RTP frame with padding greater than payload\n");
+			return -EINVAL;
+		}
+	}
+
+	switch (rtph->payload_type) {
+	case RTP_PT_GSM_FULL:
+		msg_type = GSM_TCHF_FRAME;
+		if (payload_len != 33) {
+			DEBUGPC(DMUX, "received RTP full rate frame with payload length != 32 (len = %d)\n",
+				payload_len);
+			return -EINVAL;
+		}
+		break;
+	default:
+		DEBUGPC(DMUX, "received RTP frame with unknown payload type %d\n",
+			rtph->payload_type);
+		return -EINVAL;
+	}
+
+	new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len, "GSM-DATA");
+	if (!new_msg)
+		return -ENOMEM;
+	frame = (struct gsm_data_frame *)(new_msg->data);
+	frame->msg_type = msg_type;
+	frame->callref = callref;
+	memcpy(frame->data, payload, payload_len);
+	msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
+
+	*data = new_msg;
+	return 0;
+}
+
+/* encode and send a rtp frame */
+int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
+{
+	struct rtp_sub_socket *rss = &rs->rtp;
+	struct msgb *msg;
+	struct rtp_hdr *rtph;
+	int payload_type;
+	int payload_len;
+	int duration; /* in samples */
+
+	if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
+		/* initialize sequences */
+		rs->tx_action = RTP_SEND_DOWNSTREAM;
+		rs->transmit.ssrc = random32(frame->callref);
+		rs->transmit.sequence = random();
+		rs->transmit.timestamp = random();
+	}
+
+	switch (frame->msg_type) {
+	case GSM_TCHF_FRAME:
+		payload_type = RTP_PT_GSM_FULL;
+		payload_len = 33;
+		duration = 160;
+		break;
+	default:
+		DEBUGPC(DMUX, "unsupported message type %d\n",
+			frame->msg_type);
+		return -EINVAL;
+	}
+
+	msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
+	if (!msg)
+		return -ENOMEM;
+	rtph = (struct rtp_hdr *)msg->data;
+	rtph->version = RTP_VERSION;
+	rtph->padding = 0;
+	rtph->extension = 0;
+	rtph->csrc_count = 0;
+	rtph->marker = 0;
+	rtph->payload_type = payload_type;
+	rtph->sequence = htons(rs->transmit.sequence++);
+	rtph->timestamp = htonl(rs->transmit.timestamp);
+	rs->transmit.timestamp += duration;
+	rtph->ssrc = htonl(rs->transmit.ssrc);
+	memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
+	msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
+	msgb_enqueue(&rss->tx_queue, msg);
+	rss->bfd.when |= BSC_FD_WRITE;
+
+	return 0;
+}
+
 /* iterate over all chunks in one RTCP message, look for CNAME IEs and
  * replace all of those with 'new_cname' */
 static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
@@ -123,10 +342,15 @@ static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
 	if (!mangle_rtcp_cname)
 		return 0;
 
+	printf("RTCP\n");
 	/* iterate over list of RTCP messages */
 	rtph = (struct rtcp_hdr *)msg->data;
-	while ((void *)rtph + sizeof(*rtph) < (void *)msg->data + msg->len) {
+	while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
 		old_len = (ntohs(rtph->length) + 1) * 4;
+		if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
+			DEBUGPC(DMUX, "received RTCP packet too short for lengt element\n");
+			return -EINVAL;
+		}
 		if (rtph->type == RTCP_TYPE_SDES) {
 			char new_cname[255];
 			strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
@@ -148,6 +372,7 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
 {
 	int rc;
 	struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
+	struct msgb *new_msg;
 	struct rtp_sub_socket *other_rss;
 
 	if (!msg)
@@ -184,13 +409,40 @@ static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
 		break;
 
 	case RTP_RECV_UPSTREAM:
-	case RTP_NONE:
-		/* FIXME: other cases */
-		DEBUGP(DMUX, "unhandled action: %d\n", rs->rx_action);
+		if (!rs->receive.callref || !rs->receive.net) {
+			rc = -EIO;
+			goto out_free;
+		}
+		if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+			if (!mangle_rtcp_cname) {
+				msgb_free(msg);
+				break;
+			}
+			/* modify RTCP SDES CNAME */
+			rc = rtcp_mangle(msg, rs);
+			if (rc < 0)
+				goto out_free;
+			msgb_enqueue(&rss->tx_queue, msg);
+			rss->bfd.when |= BSC_FD_WRITE;
+			break;
+		}
+		if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
+			rc = -EINVAL;
+			goto out_free;
+		}
+		rc = rtp_decode(msg, rs->receive.callref, &new_msg);
+		if (rc < 0)
+			goto out_free;
+		msgb_free(msg);
+		msgb_enqueue(&rs->receive.net->upqueue, new_msg);
+		break;
+
+	case RTP_NONE: /* if socket exists, but disabled by app */
+		msgb_free(msg);
 		break;
 	}
 
-	return rc;
+	return 0;
 
 out_free:
 	msgb_free(msg);
@@ -420,6 +672,22 @@ int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
 	return 0;
 }
 
+/* bind RTP/RTCP socket to application */
+int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, u_int32_t callref)
+{
+	DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%lu)\n",
+		this, callref);
+
+	if (callref) {
+		this->rx_action = RTP_RECV_UPSTREAM;
+		this->receive.net = net;
+		this->receive.callref = callref;
+	} else
+		this->rx_action = RTP_NONE;
+
+	return 0;
+}
+
 static void free_tx_queue(struct rtp_sub_socket *rss)
 {
 	struct msgb *msg;
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 6a19f0c..b060657 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -32,6 +32,19 @@
 #include <openbsc/debug.h>
 #include <openbsc/talloc.h>
 
+u_int8_t gsm_fr_map[] = {
+	6, 6, 5, 5, 4, 4, 3, 3,
+	7, 2, 2, 6, 3, 3, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 7, 2, 2, 6, 3, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 7, 2, 2, 6, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 3, 7, 2, 2, 6, 3,
+	3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 3, 3
+};
+
 struct map_entry {
 	struct llist_head list;
 	struct gsm_e1_subslot src, dst;
@@ -151,8 +164,6 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
 	struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
 	struct subch_mux *mx;
 	struct upqueue_entry *ue;
-	struct msgb *msg;
-	struct gsm_trau_frame *frame;
 	int rc;
 
 	/* decode TRAU, change it to downlink, re-encode */
@@ -161,19 +172,44 @@ int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
 		return rc;
 
 	if (!dst_e1_ss) {
+		struct msgb *msg;
+		struct gsm_data_frame *frame;
+		unsigned char *data;
+		int i, j, k, l, o;
 		/* frame shall be sent to upqueue */
 		if (!(ue = lookup_trau_upqueue(src_e1_ss)))
 			return -EINVAL;
 		if (!ue->callref)
 			return -EINVAL;
-		msg = msgb_alloc(sizeof(struct gsm_trau_frame) + sizeof(tf),
-				 "TRAU");
+		if (tf.c_bits[0]!=0 || tf.c_bits[1]!=0 || tf.c_bits[2]!=0 || tf.c_bits[3]!=1 || tf.c_bits[4]!=0)
+			DEBUGPC(DMUX, "illegal trau (C1-C5) %d %d %d %d %d\n", tf.c_bits[0], tf.c_bits[1],
+				tf.c_bits[2], tf.c_bits[3], tf.c_bits[4]);
+		msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
+				 "GSM-DATA");
 		if (!msg)
 			return -ENOMEM;
-		frame = (struct gsm_trau_frame *)msg->data;
-		frame->msg_type = GSM_TRAU_FRAME;
+
+		frame = (struct gsm_data_frame *)msg->data;
+		memset(frame, 0, sizeof(struct gsm_data_frame));
+		data = frame->data;
+		data[0] = 0xd << 4;
+		/* reassemble d-bits */
+		i = 0; /* counts bits */
+		j = 4; /* counts output bits */
+		k = gsm_fr_map[0]-1; /* current number bit in element */
+		l = 0; /* counts element bits */
+		o = 0; /* offset input bits */
+		while(i < 260) {
+			data[j/8] |= (tf.d_bits[k+o] << (7-(j%8)));
+			if (--k < 0) {
+				o += gsm_fr_map[l];
+				k = gsm_fr_map[++l]-1;
+			}
+			i++;
+			j++;
+		}
+		frame->msg_type = GSM_TCHF_FRAME;
 		frame->callref = ue->callref;
-		memcpy(frame->data, &tf, sizeof(tf));
 		msgb_enqueue(&ue->net->upqueue, msg);
 
 		return 0;
@@ -219,17 +255,53 @@ int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
 	return 0;
 }
 
-int trau_send_lchan(struct gsm_lchan *lchan, struct decoded_trau_frame *tf)
+int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame)
 {
 	u_int8_t trau_bits_out[TRAU_FRAME_BITS];
 	struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link;
 	struct subch_mux *mx;
+	int i, j, k, l, o;
+	unsigned char *data = frame->data;
+	struct decoded_trau_frame tf;
 
 	mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
 	if (!mx)
 		return -EINVAL;
 
-	encode_trau_frame(trau_bits_out, tf);
+	switch (frame->msg_type) {
+	case GSM_TCHF_FRAME:
+		/* set c-bits and t-bits */
+		tf.c_bits[0] = 1;
+		tf.c_bits[1] = 1;
+		tf.c_bits[2] = 1;
+		tf.c_bits[3] = 0;
+		tf.c_bits[4] = 0;
+		memset(&tf.c_bits[5], 0, 6);
+		memset(&tf.c_bits[11], 1, 10);
+		memset(&tf.t_bits[0], 1, 4);
+		/* reassemble d-bits */
+		i = 0; /* counts bits */
+		j = 4; /* counts input bits */
+		k = gsm_fr_map[0]-1; /* current number bit in element */
+		l = 0; /* counts element bits */
+		o = 0; /* offset output bits */
+		while(i < 260) {
+			tf.d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1;
+			if (--k < 0) {
+				o += gsm_fr_map[l];
+				k = gsm_fr_map[++l]-1;
+			}
+			i++;
+			j++;
+		}
+		break;
+		default:
+		DEBUGPC(DMUX, "unsupported message type %d\n",
+			frame->msg_type);
+		return -EINVAL;
+	}
+
+	encode_trau_frame(trau_bits_out, &tf);
 
 	/* and send it to the muxer */
 	return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
