Import upstream version 0.9.13
diff --git a/src/libtrau/Makefile.am b/src/libtrau/Makefile.am
new file mode 100644
index 0000000..01ed251
--- /dev/null
+++ b/src/libtrau/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS)
+
+noinst_LIBRARIES = libtrau.a
+
+libtrau_a_SOURCES = rtp_proxy.c subchan_demux.c trau_frame.c trau_mux.c trau_upqueue.c
diff --git a/src/libtrau/Makefile.in b/src/libtrau/Makefile.in
new file mode 100644
index 0000000..9da0496
--- /dev/null
+++ b/src/libtrau/Makefile.in
@@ -0,0 +1,455 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009  Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+subdir = src/libtrau
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/bscconfig.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+AR = ar
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_$(V))
+am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
+am__v_AR_0 = @echo "  AR    " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+libtrau_a_AR = $(AR) $(ARFLAGS)
+libtrau_a_LIBADD =
+am_libtrau_a_OBJECTS = rtp_proxy.$(OBJEXT) subchan_demux.$(OBJEXT) \
+	trau_frame.$(OBJEXT) trau_mux.$(OBJEXT) trau_upqueue.$(OBJEXT)
+libtrau_a_OBJECTS = $(am_libtrau_a_OBJECTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo "  CC    " $@;
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo "  CCLD  " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo "  GEN   " $@;
+SOURCES = $(libtrau_a_SOURCES)
+DIST_SOURCES = $(libtrau_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COVERAGE_CFLAGS = @COVERAGE_CFLAGS@
+COVERAGE_LDFLAGS = @COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GPRS_LIBGTP = @GPRS_LIBGTP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBOSMOCORE_CFLAGS = @LIBOSMOCORE_CFLAGS@
+LIBOSMOCORE_LIBS = @LIBOSMOCORE_LIBS@
+LIBOSMOSCCP_CFLAGS = @LIBOSMOSCCP_CFLAGS@
+LIBOSMOSCCP_LIBS = @LIBOSMOSCCP_LIBS@
+LIBOSMOVTY_CFLAGS = @LIBOSMOVTY_CFLAGS@
+LIBOSMOVTY_LIBS = @LIBOSMOVTY_LIBS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYMBOL_VISIBILITY = @SYMBOL_VISIBILITY@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build_alias = @build_alias@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host_alias = @host_alias@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS)
+AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS)
+noinst_LIBRARIES = libtrau.a
+libtrau_a_SOURCES = rtp_proxy.c subchan_demux.c trau_frame.c trau_mux.c trau_upqueue.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libtrau/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu src/libtrau/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+	-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libtrau.a: $(libtrau_a_OBJECTS) $(libtrau_a_DEPENDENCIES) 
+	$(AM_V_at)-rm -f libtrau.a
+	$(AM_V_AR)$(libtrau_a_AR) libtrau.a $(libtrau_a_OBJECTS) $(libtrau_a_LIBADD)
+	$(AM_V_at)$(RANLIB) libtrau.a
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rtp_proxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subchan_demux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trau_frame.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trau_mux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trau_upqueue.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	set x; \
+	here=`pwd`; \
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+		$(TAGS_FILES) $(LISP)
+	list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+	unique=`for i in $$list; do \
+	    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+	  done | \
+	  $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+	      END { if (nonempty) { for (i in files) print i; }; }'`; \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	  install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	  `test -z '$(STRIP)' || \
+	    echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+	clean-noinstLIBRARIES ctags distclean distclean-compile \
+	distclean-generic distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+	uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/libtrau/rtp_proxy.c b/src/libtrau/rtp_proxy.c
new file mode 100644
index 0000000..eefc0e1
--- /dev/null
+++ b/src/libtrau/rtp_proxy.c
@@ -0,0 +1,728 @@
+/* RTP proxy handling for ip.access nanoBTS */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#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 <osmocore/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <osmocore/msgb.h>
+#include <osmocore/select.h>
+#include <openbsc/debug.h>
+#include <openbsc/rtp_proxy.h>
+
+/* attempt to determine byte order */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <limits.h>
+
+#ifndef __BYTE_ORDER
+#error "__BYTE_ORDER should be defined by someone"
+#endif
+
+static LLIST_HEAD(rtp_sockets);
+
+/* should we mangle the CNAME inside SDES of RTCP packets? We disable
+ * this by default, as it seems to be not needed */
+static int mangle_rtcp_cname = 0;
+
+enum rtp_bfd_priv {
+	RTP_PRIV_NONE,
+	RTP_PRIV_RTP,
+	RTP_PRIV_RTCP
+};
+
+#define RTP_ALLOC_SIZE	1500
+
+/* according to RFC 1889 */
+struct rtcp_hdr {
+	u_int8_t byte0;
+	u_int8_t type;
+	u_int16_t length;
+} __attribute__((packed));
+
+#define RTCP_TYPE_SDES	202
+	
+#define RTCP_IE_CNAME	1
+
+/* according to RFC 3550 */
+struct rtp_hdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u_int8_t  csrc_count:4,
+		  extension:1,
+		  padding:1,
+		  version:2;
+	u_int8_t  payload_type:7,
+		  marker:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u_int8_t  version:2,
+		  padding:1,
+		  extension:1,
+		  csrc_count:4;
+	u_int8_t  marker:1,
+		  payload_type:7;
+#endif
+	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
+
+/* 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;
+	case RTP_PT_GSM_EFR:
+		msg_type = GSM_TCHF_FRAME_EFR;
+		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;
+}
+
+/* "to - from" */
+static void tv_difference(struct timeval *diff, const struct timeval *from,
+			  const struct timeval *__to)
+{
+	struct timeval _to = *__to, *to = &_to;
+
+	if (to->tv_usec < from->tv_usec) {
+		to->tv_sec -= 1;
+		to->tv_usec += 1000000;
+	}
+
+	diff->tv_usec = to->tv_usec - from->tv_usec;
+	diff->tv_sec = to->tv_sec - from->tv_sec;
+}
+
+/* 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 = rand();
+		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;
+	case GSM_TCHF_FRAME_EFR:
+		payload_type = RTP_PT_GSM_EFR;
+		payload_len = 31;
+		duration = 160;
+		break;
+	default:
+		DEBUGPC(DMUX, "unsupported message type %d\n",
+			frame->msg_type);
+		return -EINVAL;
+	}
+
+	{
+		struct timeval tv, tv_diff;
+		long int usec_diff, frame_diff;
+
+		gettimeofday(&tv, NULL);
+		tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
+		rs->transmit.last_tv = tv;
+
+		usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
+		frame_diff = (usec_diff / 20000);
+
+		if (abs(frame_diff) > 1) {
+			long int frame_diff_excess = frame_diff - 1;
+
+			LOGP(DMUX, LOGL_NOTICE,
+				"Correcting frame difference of %ld frames\n", frame_diff_excess);
+			rs->transmit.sequence += frame_diff_excess;
+			rs->transmit.timestamp += frame_diff_excess * duration;
+		}
+	}
+
+	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,
+				  u_int16_t *rtcp_len, const char *new_cname)
+{
+	u_int8_t *rtcp_end;
+	u_int8_t *cur = (u_int8_t *) rh;
+	u_int8_t tag, len = 0;
+
+	rtcp_end = cur + *rtcp_len;
+	/* move cur to end of RTP header */
+	cur += sizeof(*rh);
+
+	/* iterate over Chunks */
+	while (cur+4 < rtcp_end) {
+		/* skip four bytes SSRC/CSRC */
+		cur += 4;
+	
+		/* iterate over IE's inside the chunk */
+		while (cur+1 < rtcp_end) {
+			tag = *cur++;
+			if (tag == 0) {
+				/* end of chunk, skip additional zero */
+				while (*cur++ == 0) { }
+				break;
+			}
+			len = *cur++;
+	
+			if (tag == RTCP_IE_CNAME) {
+				/* we've found the CNAME, lets mangle it */
+				if (len < strlen(new_cname)) {
+					/* we need to make more space */
+					int increase = strlen(new_cname) - len;
+
+					msgb_push(msg, increase);
+					memmove(cur+len+increase, cur+len,
+						rtcp_end - (cur+len));
+					/* FIXME: we have to respect RTCP
+					 * padding/alignment rules! */
+					len += increase;
+					*(cur-1) += increase;
+					rtcp_end += increase;
+					*rtcp_len += increase;
+				}
+				/* copy new CNAME into message */
+				memcpy(cur, new_cname, strlen(new_cname));
+				/* FIXME: zero the padding in case new CNAME
+				 * is smaller than old one !!! */
+			}
+			cur += len;
+		}
+	}
+
+	return 0;
+}
+
+static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
+{
+	struct rtp_sub_socket *rss = &rs->rtcp;
+	struct rtcp_hdr *rtph;
+	u_int16_t old_len;
+	int rc;
+
+	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) {
+		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 "
+				"length element\n");
+			return -EINVAL;
+		}
+		if (rtph->type == RTCP_TYPE_SDES) {
+			char new_cname[255];
+			strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
+				sizeof(new_cname));
+			new_cname[sizeof(new_cname)-1] = '\0';
+			rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
+						    new_cname);
+			if (rc < 0)
+				return rc;
+		}
+		rtph = (void *)rtph + old_len;
+	}
+
+	return 0;
+}
+
+/* read from incoming RTP/RTCP socket */
+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)
+		return -ENOMEM;
+
+	rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
+	if (rc <= 0) {
+		rss->bfd.when &= ~BSC_FD_READ;
+		return rc;
+	}
+
+	msgb_put(msg, rc);
+
+	switch (rs->rx_action) {
+	case RTP_PROXY:
+		if (!rs->proxy.other_sock) {
+			rc = -EIO;
+			goto out_free;
+		}
+		if (rss->bfd.priv_nr == RTP_PRIV_RTP)
+			other_rss = &rs->proxy.other_sock->rtp;
+		else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
+			other_rss = &rs->proxy.other_sock->rtcp;
+			/* modify RTCP SDES CNAME */
+			rc = rtcp_mangle(msg, rs);
+			if (rc < 0)
+				goto out_free;
+		} else {
+			rc = -EINVAL;
+			goto out_free;
+		}
+		msgb_enqueue(&other_rss->tx_queue, msg);
+		other_rss->bfd.when |= BSC_FD_WRITE;
+		break;
+
+	case RTP_RECV_UPSTREAM:
+		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);
+		trau_tx_to_mncc(rs->receive.net, new_msg);
+		break;
+
+	case RTP_NONE: /* if socket exists, but disabled by app */
+		msgb_free(msg);
+		break;
+	}
+
+	return 0;
+
+out_free:
+	msgb_free(msg);
+	return rc;
+}
+
+/* write from tx_queue to RTP/RTCP socket */
+static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
+{
+	struct msgb *msg;
+	int written;
+
+	msg = msgb_dequeue(&rss->tx_queue);
+	if (!msg) {
+		rss->bfd.when &= ~BSC_FD_WRITE;
+		return 0;
+	}
+
+	written = write(rss->bfd.fd, msg->data, msg->len);
+	if (written < msg->len) {
+		LOGP(DMIB, LOGL_ERROR, "short write");
+		msgb_free(msg);
+		return -EIO;
+	}
+
+	msgb_free(msg);
+
+	return 0;
+}
+
+
+/* callback for the select.c:bfd_* layer */
+static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
+{
+	struct rtp_socket *rs = bfd->data;
+	struct rtp_sub_socket *rss;
+
+	switch (bfd->priv_nr) {
+	case RTP_PRIV_RTP:
+		rss = &rs->rtp;
+		break;
+	case RTP_PRIV_RTCP:
+		rss = &rs->rtcp;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (flags & BSC_FD_READ)
+		rtp_socket_read(rs, rss);
+
+	if (flags & BSC_FD_WRITE)
+		rtp_socket_write(rs, rss);
+
+	return 0;
+}
+
+static void init_rss(struct rtp_sub_socket *rss,
+		     struct rtp_socket *rs, int fd, int priv_nr)
+{
+	/* initialize bfd */
+	rss->bfd.fd = fd;
+	rss->bfd.data = rs;
+	rss->bfd.priv_nr = priv_nr;
+	rss->bfd.cb = rtp_bfd_cb;
+}
+
+struct rtp_socket *rtp_socket_create(void)
+{
+	int rc;
+	struct rtp_socket *rs;
+
+	DEBUGP(DMUX, "rtp_socket_create(): ");
+
+	rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
+	if (!rs)
+		return NULL;
+
+	INIT_LLIST_HEAD(&rs->rtp.tx_queue);
+	INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
+
+	rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (rc < 0)
+		goto out_free;
+
+	init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
+	rc = bsc_register_fd(&rs->rtp.bfd);
+	if (rc < 0)
+		goto out_rtp_socket;
+
+	rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (rc < 0)
+		goto out_rtp_bfd;
+
+	init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
+	rc = bsc_register_fd(&rs->rtcp.bfd);
+	if (rc < 0)
+		goto out_rtcp_socket;
+
+	DEBUGPC(DMUX, "success\n");
+
+	rc = rtp_socket_bind(rs, INADDR_ANY);
+	if (rc < 0)
+		goto out_rtcp_bfd;
+
+	return rs;
+
+out_rtcp_bfd:
+	bsc_unregister_fd(&rs->rtcp.bfd);
+out_rtcp_socket:
+	close(rs->rtcp.bfd.fd);
+out_rtp_bfd:
+	bsc_unregister_fd(&rs->rtp.bfd);
+out_rtp_socket:
+	close(rs->rtp.bfd.fd);
+out_free:
+	talloc_free(rs);
+	DEBUGPC(DMUX, "failed\n");
+	return NULL;
+}
+
+static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
+				u_int16_t port)
+{
+	int rc;
+	socklen_t alen = sizeof(rss->sin_local);
+
+	rss->sin_local.sin_family = AF_INET;
+	rss->sin_local.sin_addr.s_addr = htonl(ip);
+	rss->sin_local.sin_port = htons(port);
+	rss->bfd.when |= BSC_FD_READ;
+
+	rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+		  sizeof(rss->sin_local));
+	if (rc < 0)
+		return rc;
+
+	/* retrieve the address we actually bound to, in case we
+	 * passed INADDR_ANY as IP address */
+	return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+			   &alen);
+}
+
+#define RTP_PORT_BASE	30000
+static unsigned int next_udp_port = RTP_PORT_BASE;
+
+/* bind a RTP socket to a local address */
+int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
+{
+	int rc = -EIO;
+	struct in_addr ia;
+
+	ia.s_addr = htonl(ip);
+	DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
+		inet_ntoa(ia));
+
+	/* try to bind to a consecutive pair of ports */
+	for (next_udp_port = next_udp_port % 0xffff;
+	     next_udp_port < 0xffff; next_udp_port += 2) {
+		rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
+		if (rc != 0)
+			continue;
+
+		rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
+		if (rc == 0)
+			break;
+	}
+	if (rc < 0) {
+		DEBUGPC(DMUX, "failed\n");
+		return rc;
+	}
+
+	ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
+	DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
+		inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
+	return ntohs(rs->rtp.sin_local.sin_port);
+}
+
+static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
+				  u_int32_t ip, u_int16_t port)
+{
+	int rc;
+	socklen_t alen = sizeof(rss->sin_local);
+
+	rss->sin_remote.sin_family = AF_INET;
+	rss->sin_remote.sin_addr.s_addr = htonl(ip);
+	rss->sin_remote.sin_port = htons(port);
+
+	rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
+		     sizeof(rss->sin_remote));
+	if (rc < 0)
+		return rc;
+
+	return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
+			   &alen);
+}
+
+/* 'connect' a RTP socket to a remote peer */
+int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
+{
+	int rc;
+	struct in_addr ia;
+
+	ia.s_addr = htonl(ip);
+	DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
+		rs, inet_ntoa(ia), port);
+
+	rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
+	if (rc < 0)
+		return rc;
+
+	return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
+}
+
+/* bind two RTP/RTCP sockets together */
+int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
+{
+	DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
+		this, other);
+
+	this->rx_action = RTP_PROXY;
+	this->proxy.other_sock = other;
+
+	other->rx_action = RTP_PROXY;
+	other->proxy.other_sock = this;
+
+	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=%u)\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;
+	
+	while ((msg = msgb_dequeue(&rss->tx_queue)))
+		msgb_free(msg);
+}
+
+int rtp_socket_free(struct rtp_socket *rs)
+{
+	DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
+
+	/* make sure we don't leave references dangling to us */
+	if (rs->rx_action == RTP_PROXY &&
+	    rs->proxy.other_sock)
+		rs->proxy.other_sock->proxy.other_sock = NULL;
+
+	bsc_unregister_fd(&rs->rtp.bfd);
+	close(rs->rtp.bfd.fd);
+	free_tx_queue(&rs->rtp);
+
+	bsc_unregister_fd(&rs->rtcp.bfd);
+	close(rs->rtcp.bfd.fd);
+	free_tx_queue(&rs->rtcp);
+
+	talloc_free(rs);
+
+	return 0;
+}
diff --git a/src/libtrau/subchan_demux.c b/src/libtrau/subchan_demux.c
new file mode 100644
index 0000000..6bcf279
--- /dev/null
+++ b/src/libtrau/subchan_demux.c
@@ -0,0 +1,321 @@
+/* A E1 sub-channel (de)multiplexer with TRAU frame sync */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/subchan_demux.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/debug.h>
+#include <osmocore/talloc.h>
+#include <openbsc/gsm_data.h>
+
+void *tall_tqe_ctx;
+
+static inline void append_bit(struct demux_subch *sch, u_int8_t bit)
+{
+	sch->out_bitbuf[sch->out_idx++] = bit;
+}
+
+#define SYNC_HDR_BITS	16
+static const u_int8_t nullbytes[SYNC_HDR_BITS];
+
+/* check if we have just completed the 16 bit zero sync header,
+ * in accordance with GSM TS 08.60 Chapter 4.8.1 */
+static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit)
+{
+	if (bit == 0)
+		sch->consecutive_zeros++;
+	else
+		sch->consecutive_zeros = 0;
+
+	if (sch->consecutive_zeros >= SYNC_HDR_BITS) {
+		sch->consecutive_zeros = 0;
+		return 1;
+	}
+
+	return 0;
+}
+
+/* resynchronize to current location */
+static void resync_to_here(struct demux_subch *sch)
+{
+	memset(sch->out_bitbuf, 0, SYNC_HDR_BITS);
+
+	/* set index in a way that we can continue receiving bits after
+	 * the end of the SYNC header */
+	sch->out_idx = SYNC_HDR_BITS;
+	sch->in_sync = 1;
+}
+
+int subch_demux_init(struct subch_demux *dmx)
+{
+	int i;
+
+	dmx->chan_activ = 0;
+	for (i = 0; i < NR_SUBCH; i++) {
+		struct demux_subch *sch = &dmx->subch[i];
+		sch->out_idx = 0;
+		memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf));
+	}
+	return 0;
+}
+
+/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel,
+ * split it into the 16k subchannels */
+int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len)
+{
+	int i, c;
+
+	/* we avoid partially filled bytes in outbuf */
+	if (len % 4)
+		return -EINVAL;
+
+	for (i = 0; i < len; i++) {
+		u_int8_t inbyte = data[i];
+
+		for (c = 0; c < NR_SUBCH; c++) {
+			struct demux_subch *sch = &dmx->subch[c];
+			u_int8_t inbits;
+			u_int8_t bit;
+
+			/* ignore inactive subchannels */
+			if (!(dmx->chan_activ & (1 << c)))
+				continue;
+
+			inbits = inbyte >> (c << 1);
+
+			/* two bits for each subchannel */
+			if (inbits & 0x01)
+				bit = 1;
+			else
+				bit = 0;
+			append_bit(sch, bit);
+
+			if (sync_hdr_complete(sch, bit))
+				resync_to_here(sch);
+
+			if (inbits & 0x02)
+				bit = 1;
+			else
+				bit = 0;
+			append_bit(sch, bit);
+
+			if (sync_hdr_complete(sch, bit))
+				resync_to_here(sch);
+
+			/* FIXME: verify the first bit in octet 2, 4, 6, ...
+			 * according to TS 08.60 4.8.1 */
+
+			/* once we have reached TRAU_FRAME_BITS, call
+			 * the TRAU frame handler callback function */
+			if (sch->out_idx >= TRAU_FRAME_BITS) {
+				if (sch->in_sync) {
+					dmx->out_cb(dmx, c, sch->out_bitbuf,
+					    sch->out_idx, dmx->data);
+					sch->in_sync = 0;
+				}
+				sch->out_idx = 0;
+			}
+		}
+	}
+	return i;
+}
+
+int subch_demux_activate(struct subch_demux *dmx, int subch)
+{
+	if (subch >= NR_SUBCH)
+		return -EINVAL;
+
+	dmx->chan_activ |= (1 << subch);
+	return 0;
+}
+
+int subch_demux_deactivate(struct subch_demux *dmx, int subch)
+{
+	if (subch >= NR_SUBCH)
+		return -EINVAL;
+
+	dmx->chan_activ &= ~(1 << subch);
+	return 0;
+}
+
+/* MULTIPLEXER */
+
+static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr)
+{
+	/* allocate and initialize with idle pattern */
+	return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(),
+				   TRAU_FRAME_BITS);
+}
+
+/* return the requested number of bits from the specified subchannel */
+static int get_subch_bits(struct subch_mux *mx, int subch,
+			  u_int8_t *bits, int num_requested)
+{
+	struct mux_subch *sch = &mx->subch[subch];
+	int num_bits = 0;
+
+	while (num_bits < num_requested) {
+		struct subch_txq_entry *txe;
+		int num_bits_left;
+		int num_bits_thistime;
+
+		/* make sure we have a valid entry at top of tx queue.
+		 * if not, add an idle frame */
+		if (llist_empty(&sch->tx_queue))
+			alloc_add_idle_frame(mx, subch);
+	
+		if (llist_empty(&sch->tx_queue))
+			return -EIO;
+
+		txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
+		num_bits_left = txe->bit_len - txe->next_bit;
+
+		if (num_bits_left < num_requested)
+			num_bits_thistime = num_bits_left;
+		else
+			num_bits_thistime = num_requested;
+
+		/* pull the bits from the txe */
+		memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime);
+		txe->next_bit += num_bits_thistime;
+
+		/* free the tx_queue entry if it is fully consumed */
+		if (txe->next_bit >= txe->bit_len) {
+			llist_del(&txe->list);
+			talloc_free(txe);
+		}
+
+		/* increment global number of bits dequeued */
+		num_bits += num_bits_thistime;
+	}
+
+	return num_requested;
+}
+
+/* compact an array of 8 single-bit bytes into one byte of 8 bits */
+static u_int8_t compact_bits(const u_int8_t *bits)
+{
+	u_int8_t ret = 0;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		ret |= (bits[i] ? 1 : 0) << i;
+
+	return ret;
+}
+
+/* obtain a single output byte from the subchannel muxer */
+static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte)
+{
+	u_int8_t bits[8];
+	int rc;
+
+	/* combine two bits of every subchan */
+	rc = get_subch_bits(mx, 0, &bits[0], 2);
+	rc = get_subch_bits(mx, 1, &bits[2], 2);
+	rc = get_subch_bits(mx, 2, &bits[4], 2);
+	rc = get_subch_bits(mx, 3, &bits[6], 2);
+
+	*byte = compact_bits(bits);
+
+	return rc;
+}
+
+/* Request the output of some muxed bytes from the subchan muxer */
+int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		int rc;
+		rc = mux_output_byte(mx, &data[i]);
+		if (rc < 0)
+			break;
+	}
+	return i;
+}
+
+static int llist_len(struct llist_head *head)
+{
+	struct llist_head *entry;
+	int i = 0;
+
+	llist_for_each(entry, head)
+		i++;
+
+	return i;
+}
+
+/* evict the 'num_evict' number of oldest entries in the queue */
+static void tx_queue_evict(struct mux_subch *sch, int num_evict)
+{
+	struct subch_txq_entry *tqe;
+	int i;
+
+	for (i = 0; i < num_evict; i++) {
+		if (llist_empty(&sch->tx_queue))
+			return;
+
+		tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list);
+		llist_del(&tqe->list);
+		talloc_free(tqe);
+	}
+}
+
+/* enqueue some data into the tx_queue of a given subchannel */
+int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data,
+			int len)
+{
+	struct mux_subch *sch = &mx->subch[s_nr];
+	int list_len = llist_len(&sch->tx_queue);
+	struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx,
+							sizeof(*tqe) + len);
+	if (!tqe)
+		return -ENOMEM;
+
+	tqe->bit_len = len;
+	memcpy(tqe->bits, data, len);
+
+	if (list_len > 2)
+		tx_queue_evict(sch, list_len-2);
+
+	llist_add_tail(&tqe->list, &sch->tx_queue);
+
+	return 0;
+}
+
+/* initialize one subchannel muxer instance */
+int subchan_mux_init(struct subch_mux *mx)
+{
+	int i;
+
+	memset(mx, 0, sizeof(*mx));
+	for (i = 0; i < NR_SUBCH; i++) {
+		struct mux_subch *sch = &mx->subch[i];
+		INIT_LLIST_HEAD(&sch->tx_queue);
+	}
+
+	return 0;
+}
diff --git a/src/libtrau/trau_frame.c b/src/libtrau/trau_frame.c
new file mode 100644
index 0000000..d4d6447
--- /dev/null
+++ b/src/libtrau/trau_frame.c
@@ -0,0 +1,260 @@
+/* TRAU frame handling according to GSM TS 08.60 */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openbsc/trau_frame.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/debug.h>
+
+static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num)
+{
+	int i;
+	u_int32_t ret = 0;
+
+	for (i = offset; i < offset + num; i++) {
+		ret = ret << 1;
+		if (bitbuf[i])
+			ret |= 1;
+	}
+	return ret;
+}
+
+/* Decode according to 3.1.1 */
+static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+	int i;
+	int d_idx = 0;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits+0, trau_bits+17, 15);
+	/* C16 .. C21 */
+	memcpy(fr->c_bits+15, trau_bits+310, 6);
+	/* T1 .. T4 */
+	memcpy(fr->t_bits+0, trau_bits+316, 4);
+	/* D1 .. D255 */
+	for (i = 32; i < 304; i+= 16) {
+		memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
+		d_idx += 15;
+	}
+	/* D256 .. D260 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 305, 5);
+}
+
+/* Decode according to 3.1.2 */
+static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+	int i;
+	int d_idx = 0;
+
+	/* C1 .. C15 */
+	memcpy(fr->c_bits+0, trau_bits+17, 15);
+	/* C16 .. C25 */
+	memcpy(fr->c_bits+15, trau_bits+33, 10);
+	/* T1 .. T4 */
+	memcpy(fr->t_bits+0, trau_bits+316, 4);
+	/* D1 .. D5 */
+	memcpy(fr->d_bits, trau_bits+43, 5);
+	/* D6 .. D245 */
+	for (i = 48; i < 304; i += 16) {
+		memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15);
+		d_idx += 15;
+	}
+	/* D246 .. D256 */
+	memcpy(fr->d_bits + d_idx, trau_bits + 305, 11);
+}
+
+int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits)
+{
+	u_int8_t cbits5 = get_bits(trau_bits, 17, 5);
+
+	switch (cbits5) {
+	case TRAU_FT_FR_UP:
+	case TRAU_FT_FR_DOWN:
+	case TRAU_FT_IDLE_UP:
+	case TRAU_FT_IDLE_DOWN:
+	case TRAU_FT_EFR:
+		decode_fr(fr, trau_bits);
+		break;
+	case TRAU_FT_AMR:
+		decode_amr(fr, trau_bits);
+		break;
+	case TRAU_FT_OM_UP:
+	case TRAU_FT_OM_DOWN:
+	case TRAU_FT_DATA_UP:
+	case TRAU_FT_DATA_DOWN:
+	case TRAU_FT_D145_SYNC:
+	case TRAU_FT_EDATA:
+		LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU "
+			"Frame Type 0x%02x\n", cbits5);
+		return -1;
+		break;
+	default:
+		LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU "
+			"Frame Type 0x%02x\n", cbits5);
+		return -1;
+		break;
+	}
+
+	return 0;
+}
+
+const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 };
+const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 };
+
+/* modify an uplink TRAU frame so we can send it downlink */
+int trau_frame_up2down(struct decoded_trau_frame *fr)
+{
+	u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
+
+	switch (cbits5) {
+	case TRAU_FT_FR_UP:
+		memcpy(fr->c_bits, ft_fr_down_bits, 5);
+		/* clear time alignment */
+		memset(fr->c_bits+5, 0, 6);
+		/* FIXME: SP / BFI in case of DTx */
+		/* C12 .. C21 are spare and coded as '1' */
+		memset(fr->c_bits+11, 0x01, 10);
+		break;
+	case TRAU_FT_EFR:
+		/* clear time alignment */
+		memset(fr->c_bits+5, 0, 6);
+		/* FIXME: set UFE appropriately */
+		/* FIXME: SP / BFI in case of DTx */
+		break;
+	case TRAU_FT_IDLE_UP:
+		memcpy(fr->c_bits, ft_idle_down_bits, 5);
+		/* clear time alignment */
+		memset(fr->c_bits+5, 0, 6);
+		/* FIXME: SP / BFI in case of DTx */
+		/* C12 .. C21 are spare and coded as '1' */
+		memset(fr->c_bits+11, 0x01, 10);
+		break;
+	case TRAU_FT_FR_DOWN:
+	case TRAU_FT_IDLE_DOWN:
+	case TRAU_FT_OM_DOWN:
+	case TRAU_FT_DATA_DOWN:
+		/* we cannot convert a downlink to a downlink frame */
+		return -EINVAL;
+		break;
+	case TRAU_FT_AMR:
+	case TRAU_FT_OM_UP:
+	case TRAU_FT_DATA_UP:
+	case TRAU_FT_D145_SYNC:
+	case TRAU_FT_EDATA:
+		LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+			"0x%02x\n", cbits5);
+		return -1;
+		break;
+	default:
+		LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+			"0x%02x\n", cbits5);
+		return -1;
+		break;
+	}
+
+	return 0;
+
+}
+
+static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
+{
+	int i;
+	int d_idx = 0;
+
+	trau_bits[16] = 1;
+	/* C1 .. C15 */
+	memcpy(trau_bits+17, fr->c_bits+0, 15);
+	/* D1 .. D255 */
+	for (i = 32; i < 304; i+= 16) {
+		trau_bits[i] = 1;
+		memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15);
+		d_idx += 15;
+	}
+	/* D256 .. D260 */
+	trau_bits[304] = 1;
+	memcpy(trau_bits + 305, fr->d_bits + d_idx, 5);
+	/* C16 .. C21 */
+	memcpy(trau_bits+310, fr->c_bits+15, 6);
+
+	/* FIXME: handle timing adjustment */
+
+	/* T1 .. T4 */
+	memcpy(trau_bits+316, fr->t_bits+0, 4);
+}
+
+
+int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr)
+{
+	u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5);
+	
+	/* 16 bits of sync header */
+	memset(trau_bits, 0, 16);
+
+	switch (cbits5) {
+	case TRAU_FT_FR_UP:
+	case TRAU_FT_FR_DOWN:
+	case TRAU_FT_IDLE_UP:
+	case TRAU_FT_IDLE_DOWN:
+	case TRAU_FT_EFR:
+		encode_fr(trau_bits, fr);
+		break;
+	case TRAU_FT_AMR:
+	case TRAU_FT_OM_UP:
+	case TRAU_FT_OM_DOWN:
+	case TRAU_FT_DATA_UP:
+	case TRAU_FT_DATA_DOWN:
+	case TRAU_FT_D145_SYNC:
+	case TRAU_FT_EDATA:
+		LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type "
+			"0x%02x\n", cbits5);
+		return -1;
+		break;
+	default:
+		LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type "
+			"0x%02x\n", cbits5);
+		return -1;
+		break;
+	}
+
+	return 0;
+}
+
+static struct decoded_trau_frame fr_idle_frame = {
+	.c_bits = { 0, 1, 1, 1, 0 },	/* IDLE DOWNLINK 3.5.5 */
+	.t_bits = { 1, 1, 1, 1 },
+};
+static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS];
+static int dbits_initted;
+
+u_int8_t *trau_idle_frame(void)
+{
+	/* only initialize during the first call */
+	if (!dbits_initted) {
+		/* set all D-bits to 1 */
+		memset(&fr_idle_frame.d_bits, 0x01, 260);
+		encode_fr(encoded_idle_frame, &fr_idle_frame);
+	}
+	return encoded_idle_frame;
+}
diff --git a/src/libtrau/trau_mux.c b/src/libtrau/trau_mux.c
new file mode 100644
index 0000000..712e22d
--- /dev/null
+++ b/src/libtrau/trau_mux.c
@@ -0,0 +1,312 @@
+/* Simple TRAU frame reflector to route voice calls */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/trau_frame.h>
+#include <openbsc/trau_mux.h>
+#include <openbsc/subchan_demux.h>
+#include <openbsc/e1_input.h>
+#include <openbsc/debug.h>
+#include <osmocore/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;
+};
+
+struct upqueue_entry {
+	struct llist_head list;
+	struct gsm_network *net;
+	struct gsm_e1_subslot src;
+	u_int32_t callref;
+};
+
+static LLIST_HEAD(ss_map);
+static LLIST_HEAD(ss_upqueue);
+
+void *tall_map_ctx, *tall_upq_ctx;
+
+/* map one particular subslot to another subslot */
+int trau_mux_map(const struct gsm_e1_subslot *src,
+		 const struct gsm_e1_subslot *dst)
+{
+	struct map_entry *me;
+
+	me = talloc(tall_map_ctx, struct map_entry);
+	if (!me) {
+		LOGP(DMIB, LOGL_FATAL, "Out of memory\n");
+		return -ENOMEM;
+	}
+
+	DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) "
+		"and (e1=%u,ts=%u,ss=%u)\n",
+		src->e1_nr, src->e1_ts, src->e1_ts_ss,
+		dst->e1_nr, dst->e1_ts, dst->e1_ts_ss);
+
+	/* make sure to get rid of any stale old mappings */
+	trau_mux_unmap(src, 0);
+	trau_mux_unmap(dst, 0);
+
+	memcpy(&me->src, src, sizeof(me->src));
+	memcpy(&me->dst, dst, sizeof(me->dst));
+	llist_add(&me->list, &ss_map);
+
+	return 0;
+}
+
+int trau_mux_map_lchan(const struct gsm_lchan *src,	
+			const struct gsm_lchan *dst)
+{
+	struct gsm_e1_subslot *src_ss, *dst_ss;
+
+	src_ss = &src->ts->e1_link;
+	dst_ss = &dst->ts->e1_link;
+
+	return trau_mux_map(src_ss, dst_ss);
+}
+
+
+/* unmap one particular subslot from another subslot */
+int trau_mux_unmap(const struct gsm_e1_subslot *ss, u_int32_t callref)
+{
+	struct map_entry *me, *me2;
+	struct upqueue_entry *ue, *ue2;
+
+	if (ss)
+		llist_for_each_entry_safe(me, me2, &ss_map, list) {
+			if (!memcmp(&me->src, ss, sizeof(*ss)) ||
+			    !memcmp(&me->dst, ss, sizeof(*ss))) {
+				llist_del(&me->list);
+				return 0;
+			}
+		}
+	llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) {
+		if (ue->callref == callref) {
+			llist_del(&ue->list);
+			return 0;
+		}
+		if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) {
+			llist_del(&ue->list);
+			return 0;
+		}
+	}
+	return -ENOENT;
+}
+
+/* look-up an enty in the TRAU mux map */
+static struct gsm_e1_subslot *
+lookup_trau_mux_map(const struct gsm_e1_subslot *src)
+{
+	struct map_entry *me;
+
+	llist_for_each_entry(me, &ss_map, list) {
+		if (!memcmp(&me->src, src, sizeof(*src)))
+			return &me->dst;
+		if (!memcmp(&me->dst, src, sizeof(*src)))
+			return &me->src;
+	}
+	return NULL;
+}
+
+/* look-up an enty in the TRAU upqueue */
+struct upqueue_entry *
+lookup_trau_upqueue(const struct gsm_e1_subslot *src)
+{
+	struct upqueue_entry *ue;
+
+	llist_for_each_entry(ue, &ss_upqueue, list) {
+		if (!memcmp(&ue->src, src, sizeof(*src)))
+			return ue;
+	}
+	return NULL;
+}
+
+static const u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 };
+
+/* we get called by subchan_demux */
+int trau_mux_input(struct gsm_e1_subslot *src_e1_ss,
+		   const u_int8_t *trau_bits, int num_bits)
+{
+	struct decoded_trau_frame tf;
+	u_int8_t trau_bits_out[TRAU_FRAME_BITS];
+	struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss);
+	struct subch_mux *mx;
+	struct upqueue_entry *ue;
+	int rc;
+
+	/* decode TRAU, change it to downlink, re-encode */
+	rc = decode_trau_frame(&tf, trau_bits);
+	if (rc)
+		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;
+		if (memcmp(tf.c_bits, c_bits_check, sizeof(c_bits_check)))
+			DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n",
+				hexdump(tf.c_bits, sizeof(c_bits_check)));
+		msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33,
+				 "GSM-DATA");
+		if (!msg)
+			return -ENOMEM;
+
+		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;
+		trau_tx_to_mncc(ue->net, msg);
+
+		return 0;
+	}
+
+	mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts);
+	if (!mx)
+		return -EINVAL;
+
+	trau_frame_up2down(&tf);
+	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,
+				   TRAU_FRAME_BITS);
+}
+
+/* add receiver instance for lchan and callref */
+int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_t callref)
+{
+	struct gsm_e1_subslot *src_ss;
+	struct upqueue_entry *ue;
+
+	ue = talloc(tall_upq_ctx, struct upqueue_entry);
+	if (!ue)
+		return -ENOMEM;
+
+	src_ss = &lchan->ts->e1_link;
+
+	DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) "
+		"and (callref 0x%x)\n",
+		src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss,
+		callref);
+
+	/* make sure to get rid of any stale old mappings */
+	trau_mux_unmap(src_ss, callref);
+
+	memcpy(&ue->src, src_ss, sizeof(ue->src));
+	ue->net = lchan->ts->trx->bts->network;
+	ue->callref = callref;
+	llist_add(&ue->list, &ss_upqueue);
+
+	return 0;
+}
+
+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;
+
+	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,
+				   TRAU_FRAME_BITS);
+}
diff --git a/src/libtrau/trau_upqueue.c b/src/libtrau/trau_upqueue.c
new file mode 100644
index 0000000..f8edaf0
--- /dev/null
+++ b/src/libtrau/trau_upqueue.c
@@ -0,0 +1,27 @@
+/* trau_upqueue.c - Pass msgb's up the chain */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <openbsc/mncc.h>
+#include <openbsc/gsm_data.h>
+
+void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg)
+{
+	net->mncc_recv(net, msg);
+}