Merge commit 'origin/master'
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index 86f056d..adef573 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -2,4 +2,4 @@
 		 gsm_subscriber.h linuxlist.h msgb.h select.h tlv.h gsm_04_11.h \
 		 timer.h misdn.h chan_alloc.h telnet_interface.h paging.h \
 		 subchan_demux.h trau_frame.h e1_input.h trau_mux.h signal.h \
-		 gsm_utils.h ipaccess.h rs232.h openbscdefines.h
+		 gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 84a1697..9e4d590 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -319,11 +319,14 @@
 #define RSL_ERRCLS_PROTO_ERROR		0x60
 #define RSL_ERRCLS_INTERWORKING		0x70
 
+/* normal event */
 #define RSL_ERR_RADIO_IF_FAIL		0x00
 #define RSL_ERR_RADIO_LINK_FAIL		0x01
 #define RSL_ERR_HANDOVER_ACC_FAIL	0x02
 #define RSL_ERR_TALKER_ACC_FAIL		0x03
 #define RSL_ERR_OM_INTERVENTION		0x07
+#define RSL_ERR_NORMAL_UNSPEC		0x0f
+/* resource unavailable */
 #define RSL_ERR_EQUIPMENT_FAIL		0x20
 #define RSL_ERR_RR_UNAVAIL		0x21
 #define RSL_ERR_TERR_CH_FAIL		0x22
@@ -331,15 +334,19 @@
 #define RSL_ERR_ACCH_OVERLOAD		0x24
 #define RSL_ERR_PROCESSOR_OVERLOAD	0x25
 #define RSL_ERR_RES_UNAVAIL		0x2f
+/* service or option not available */
 #define RSL_ERR_TRANSC_UNAVAIL		0x30
 #define RSL_ERR_SERV_OPT_UNAVAIL	0x3f
+/* service or option not implemented */
 #define RSL_ERR_ENCR_UNIMPL		0x40
-#define RSL_ERR_SEV_OPT_UNIMPL		0x4f
+#define RSL_ERR_SERV_OPT_UNIMPL		0x4f
+/* invalid message */
 #define RSL_ERR_RCH_ALR_ACTV_ALLOC	0x50
 #define RSL_ERR_INVALID_MESSAGE		0x5f
+/* protocol error */
 #define RSL_ERR_MSG_DISCR		0x60
 #define RSL_ERR_MSG_TYPE		0x61
-#define RSL_ERR_MSG_SEQA		0x62
+#define RSL_ERR_MSG_SEQ			0x62
 #define RSL_ERR_IE_ERROR		0x63
 #define RSL_ERR_MAND_IE_ERROR		0x64
 #define RSL_ERR_OPT_IE_ERROR		0x65
@@ -347,6 +354,7 @@
 #define RSL_ERR_IE_LENGTH		0x67
 #define RSL_ERR_IE_CONTENT		0x68
 #define RSL_ERR_PROTO			0x6f
+/* interworking */
 #define RSL_ERR_INTERWORKING		0x7f
 
 /* Chapter 9.3.30 */
@@ -477,6 +485,7 @@
 
 /* to be provided by external code */
 int abis_rsl_sendmsg(struct msgb *msg);
+int rsl_deact_sacch(struct gsm_lchan *lchan);
 int rsl_chan_release(struct gsm_lchan *lchan);
 
 /* BCCH related code */
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index 61a3ac4..c9b7265 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -31,14 +31,17 @@
 int db_fini();
 
 /* subscriber management */
-struct gsm_subscriber* db_create_subscriber(char *imsi);
-struct gsm_subscriber* db_get_subscriber(enum gsm_subscriber_field field, const char *subscr);
+struct gsm_subscriber* db_create_subscriber(struct gsm_network *net,
+					    char *imsi);
+struct gsm_subscriber* db_get_subscriber(struct gsm_network *net,
+					 enum gsm_subscriber_field field,
+					 const char *subscr);
 int db_sync_subscriber(struct gsm_subscriber* subscriber);
 int db_subscriber_alloc_tmsi(struct gsm_subscriber* subscriber);
 int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char *imei);
 
 /* SMS store-and-forward */
 int db_sms_store(struct gsm_sms *sms);
-struct gsm_sms *db_sms_get_unsent(int min_id);
+struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id);
 int db_sms_mark_sent(struct gsm_sms *sms);
 #endif /* _DB_H */
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 2fe23d1..2ed95f3 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -646,6 +646,7 @@
 struct gsm_bts;
 struct gsm_subscriber;
 struct gsm_network;
+struct gsm_trans;
 
 /* config options controlling the behaviour of the lower leves */
 void gsm0408_allow_everyone(int allow);
@@ -659,7 +660,7 @@
 
 int gsm48_tx_mm_info(struct gsm_lchan *lchan);
 struct msgb *gsm48_msgb_alloc(void);
-int gsm48_sendmsg(struct msgb *msg);
+int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans);
 int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi);
 
 int gsm48_send_rr_release(struct gsm_lchan *lchan);
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index de3f1f5..e6dffa8 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -98,35 +98,43 @@
 struct gsm_lchan;
 struct gsm_subscriber;
 struct gsm_mncc;
+struct rtp_socket;
 
 /* One transaction */
 struct gsm_trans {
 	/* Entry in list of all transactions */
 	struct llist_head entry;
 
-	/* Network */
-	struct gsm_network *network;
+	/* The protocol within which we live */
+	u_int8_t protocol;
 
 	/* The current transaction ID */
 	u_int8_t transaction_id;
 	
-	/* The LCHAN that we're part of */
-	struct gsm_lchan *lchan;
-
-	/* To whom we are allocated at the moment */
+	/* To whom we belong, unique identifier of remote MM entity */
 	struct gsm_subscriber *subscr;
 
-	/* reference */
+	/* The LCHAN that we're currently using to transmit messages */
+	struct gsm_lchan *lchan;
+
+	/* reference from MNCC or other application */
 	u_int32_t callref;
 
-	/* current call state */
-	int state;
+	union {
+		struct {
 
-	/* current timer and message queue */
-	int Tcurrent;			/* current CC timer */
-	int T308_second;		/* used to send release again */
-        struct timer_list cc_timer;
-	struct gsm_mncc cc_msg;		/* stores setup/disconnect/release message */
+			/* current call state */
+			int state;
+
+			/* current timer and message queue */
+			int Tcurrent;		/* current CC timer */
+			int T308_second;	/* used to send release again */
+			struct timer_list timer;
+			struct gsm_mncc msg;	/* stores setup/disconnect/release message */
+		} cc;
+		struct {
+		} sms;
+	};
 };
 
 
@@ -207,6 +215,7 @@
 		u_int16_t bound_port;
 		u_int8_t rtp_payload2;
 		u_int16_t conn_id;
+		struct rtp_socket *rtp_socket;
 	} abis_ip;
 
 	struct gsm_lchan lchan[TS_MAX_LCHAN];
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 4a23115..00eeb32 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -60,9 +60,12 @@
 
 struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr);
 struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr);
-struct gsm_subscriber *subscr_get_by_tmsi(const char *tmsi);
-struct gsm_subscriber *subscr_get_by_imsi(const char *imsi);
-struct gsm_subscriber *subscr_get_by_extension(const char *ext);
+struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net,
+					  const char *tmsi);
+struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net,
+					  const char *imsi);
+struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
+					       const char *ext);
 int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
 void subscr_put_channel(struct gsm_lchan *lchan);
 void subscr_get_channel(struct gsm_subscriber *subscr,
diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h
new file mode 100644
index 0000000..e9fc157
--- /dev/null
+++ b/openbsc/include/openbsc/rtp_proxy.h
@@ -0,0 +1,70 @@
+#ifndef _RTP_PROXY_H
+#define _RTP_PROXY_H
+
+/* 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+
+#include <netinet/in.h>
+
+#include <openbsc/linuxlist.h>
+#include <openbsc/select.h>
+
+enum rtp_rx_action {
+	RTP_NONE,
+	RTP_PROXY,
+	RTP_RECV_UPSTREAM,
+};
+
+struct rtp_sub_socket {
+	struct sockaddr_in sin_local;
+	struct sockaddr_in sin_remote;
+
+	struct bsc_fd bfd;
+	/* linked list of to-be-transmitted msgb's */
+	struct llist_head tx_queue;
+};
+
+struct rtp_socket {
+	struct llist_head list;
+
+	struct rtp_sub_socket rtp;
+	struct rtp_sub_socket rtcp;
+
+	/* what should we do on receive? */
+	enum rtp_rx_action rx_action;
+	union {
+		struct {
+			struct rtp_socket *other_sock;
+		} proxy;
+		struct {
+			void (*recv_cb)(struct msgb *msg);
+		} receive;
+	};
+};
+
+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_free(struct rtp_socket *rs);
+
+#endif /* _RTP_PROXY_H */
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index b41182d..2ce812d 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -48,6 +48,7 @@
 /* SS_ABISIP signals */
 enum signal_abisip {
 	S_ABISIP_BIND_ACK,
+	S_ABISIP_DISC_IND,
 };
 
 /* SS_NM signals */
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
new file mode 100644
index 0000000..1450dbc
--- /dev/null
+++ b/openbsc/include/openbsc/transaction.h
@@ -0,0 +1,18 @@
+#ifndef _TRANSACT_H
+#define _TRANSACT_H
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+
+struct gsm_trans *trans_find_by_id(struct gsm_lchan *lchan, u_int8_t trans_id);
+struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
+					u_int32_t callref);
+
+struct gsm_trans *trans_alloc(struct gsm_subscriber *subscr,
+			      u_int8_t protocol, u_int8_t trans_id,
+			      u_int32_t callref);
+void trans_free(struct gsm_trans *trans);
+
+int trans_assign_trans_id(struct gsm_subscriber *subscr,
+			  u_int8_t protocol, u_int8_t ti_flag);
+#endif
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index c0ac63c..ed63c8e 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -9,7 +9,8 @@
 		gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
 		gsm_04_11.c telnet_interface.c subchan_demux.c \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
-		input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c
+		input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
+		transaction.c rtp_proxy.c
 
 libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
 
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index f2383e2..dec4b29 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -1643,7 +1643,7 @@
 		msgb_tv_put(msg, NM_ATT_HSN, 0x00);
 		msgb_tv_put(msg, NM_ATT_MAIO, 0x00);
 	}
-	msgb_tv_put(msg, NM_ATT_TSC, 0x07);	/* training sequence */
+	msgb_tv_put(msg, NM_ATT_TSC, bts->tsc);	/* training sequence */
 	if (bts->type == GSM_BTS_TYPE_BS11)
 		msgb_tlv_put(msg, 0x59, 1, &zero);
 
@@ -2056,10 +2056,6 @@
 	FILE *swl;
 	int rc = 0;
 
-	if (!tall_fle_ctx)
-		tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 1, 
-						  "bs11_file_list_entry");
-
 	swl = fopen(bs11_sw->swl_fname, "r");
 	if (!swl)
 		return -ENODEV;
@@ -2373,3 +2369,10 @@
 {
 	return __simple_cmd(bts, NM_MT_IPACC_RESTART);
 }
+
+
+static __attribute__((constructor)) void on_dso_load_abis_nm(void)
+{
+	tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 1, 
+					  "bs11_file_list_entry");
+}
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index df95e79..56c3f71 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -314,18 +314,55 @@
 		memset(out+len, 0x2b, MACBLOCK_SIZE-len);
 }
 
-static void print_rsl_cause(u_int8_t *cause_tlv)
+static const char *rsl_err_vals[0xff] = {
+	[RSL_ERR_RADIO_IF_FAIL] =	"Radio Interface Failure",
+	[RSL_ERR_RADIO_LINK_FAIL] =	"Radio Link Failure",
+	[RSL_ERR_HANDOVER_ACC_FAIL] =	"Handover Access Failure",
+	[RSL_ERR_TALKER_ACC_FAIL] =	"Talker Access Failure",
+	[RSL_ERR_OM_INTERVENTION] =	"O&M Intervention",
+	[RSL_ERR_NORMAL_UNSPEC] =	"Normal event, unspecified",
+	[RSL_ERR_EQUIPMENT_FAIL] =	"Equipment Failure",
+	[RSL_ERR_RR_UNAVAIL] =		"Radio Resource not available",
+	[RSL_ERR_TERR_CH_FAIL] =	"Terrestrial Channel Failure",
+	[RSL_ERR_CCCH_OVERLOAD] =	"CCCH Overload",
+	[RSL_ERR_ACCH_OVERLOAD] =	"ACCH Overload",
+	[RSL_ERR_PROCESSOR_OVERLOAD] =	"Processor Overload",
+	[RSL_ERR_RES_UNAVAIL] =		"Resource not available, unspecified",
+	[RSL_ERR_TRANSC_UNAVAIL] =	"Transcoding not available",
+	[RSL_ERR_SERV_OPT_UNAVAIL] =	"Service or Option not available",
+	[RSL_ERR_ENCR_UNIMPL] =		"Encryption algorithm not implemented",
+	[RSL_ERR_SERV_OPT_UNIMPL] =	"Service or Option not implemented",
+	[RSL_ERR_RCH_ALR_ACTV_ALLOC] =	"Radio channel already activated",
+	[RSL_ERR_INVALID_MESSAGE] =	"Invalid Message, unspecified",
+	[RSL_ERR_MSG_DISCR] =		"Message Discriminator Error",
+	[RSL_ERR_MSG_TYPE] =		"Message Type Error",
+	[RSL_ERR_MSG_SEQ] =		"Message Sequence Error",
+	[RSL_ERR_IE_ERROR] =		"General IE error",
+	[RSL_ERR_MAND_IE_ERROR] =	"Mandatory IE error",
+	[RSL_ERR_OPT_IE_ERROR] =	"Optional IE error",
+	[RSL_ERR_IE_NONEXIST] =		"IE non-existent",
+	[RSL_ERR_IE_LENGTH] =		"IE length error",
+	[RSL_ERR_IE_CONTENT] =		"IE content error",
+	[RSL_ERR_PROTO] =		"Protocol error, unspecified",
+	[RSL_ERR_INTERWORKING] =	"Interworking error, unspecified",
+};
+
+static const char *rsl_err_name(u_int8_t err)
 {
-	u_int8_t cause_len;
+	if (rsl_err_vals[err])
+		return rsl_err_vals[err];
+	else
+		return "unknown";
+}
+
+static void print_rsl_cause(const u_int8_t *cause_v, u_int8_t cause_len)
+{
 	int i;
 
-	if (cause_tlv[0] != RSL_IE_CAUSE)
-		return;
-
-	cause_len = cause_tlv[1];
-	DEBUGPC(DRSL, "CAUSE: ");
-	for (i = 0; i < cause_len; i++) 
-		DEBUGPC(DRSL, "%02x ", cause_tlv[2+i]);
+	DEBUGPC(DRSL, "CAUSE=0x%02x(%s) ",
+		cause_v[0], rsl_err_name(cause_v[0]));
+	for (i = 1; i < cause_len-1; i++) 
+		DEBUGPC(DRSL, "%02x ", cause_v[i]);
 }
 
 /* Send a BCCH_INFO message as per Chapter 8.5.1 */
@@ -652,6 +689,25 @@
 	return abis_rsl_sendmsg(msg);
 }
 
+/* Chapter 8.4.5 */
+int rsl_deact_sacch(struct gsm_lchan *lchan)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct msgb *msg = rsl_msgb_alloc();
+
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH);
+	dh->chan_nr = lchan2chan_nr(lchan);
+
+	msg->lchan = lchan;
+	msg->trx = lchan->ts->trx;
+
+	DEBUGP(DRSL, "DEACTivate SACCH CMD channel=%s chan_nr=0x%02x\n",
+		gsm_ts_name(lchan->ts), dh->chan_nr);
+
+	return abis_rsl_sendmsg(msg);
+}
+
 /* Chapter 9.1.7 of 04.08 */
 int rsl_chan_release(struct gsm_lchan *lchan)
 {
@@ -798,8 +854,9 @@
 
 	rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
 	if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
-		DEBUGPC(DRSL, "CAUSE=0x%02x ", *TLVP_VAL(&tp, RSL_IE_CAUSE));
-	
+		print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
+				TLVP_LEN(&tp, RSL_IE_CAUSE));
+
 	return 0;
 }
 
@@ -810,10 +867,13 @@
 	struct tlv_parsed tp;
 
 	DEBUGPC(DRSL, "CONNECTION FAIL: ");
-	print_rsl_cause(dh->data);
 
 	rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
 
+	if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
+		print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
+				TLVP_LEN(&tp, RSL_IE_CAUSE));
+
 	if (msg->trx->bts->type == GSM_BTS_TYPE_BS11) {
 		/* FIXME: we have no idea what cause 0x18 is !!! */
 		if (TLVP_PRESENT(&tp, RSL_IE_CAUSE) &&
@@ -935,9 +995,16 @@
 static int rsl_rx_error_rep(struct msgb *msg)
 {
 	struct abis_rsl_common_hdr *rslh = msgb_l2(msg);
+	struct tlv_parsed tp;
 
 	DEBUGP(DRSL, "ERROR REPORT ");
-	print_rsl_cause(rslh->data);
+
+	rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh));
+
+	if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
+		print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
+				TLVP_LEN(&tp, RSL_IE_CAUSE));
+
 	DEBUGPC(DRSL, "\n");
 
 	return 0;
@@ -1025,7 +1092,7 @@
 	ia.chan_desc.h0.h = 0;
 	ia.chan_desc.h0.arfcn_high = arfcn >> 8;
 	ia.chan_desc.h0.arfcn_low = arfcn & 0xff;
-	ia.chan_desc.h0.tsc = 7;
+	ia.chan_desc.h0.tsc = bts->tsc;
 	/* use request reference extracted from CHAN_RQD */
 	memcpy(&ia.req_ref, rqd_ref, sizeof(ia.req_ref));
 	ia.timing_advance = rqd_ta;
@@ -1175,17 +1242,45 @@
 	return rc;
 }
 
+static u_int8_t ipa_smod_s_for_tch_mode(u_int8_t tch_mode)
+{
+#if 0
+	switch (tch_mode) {
+	case GSM48_CMODE_SPEECH_V1:
+		return 0x00;
+	case GSM48_CMODE_SPEECH_EFR:
+		return 0x01;
+	case GSM48_CMODE_SPEECH_AMR:
+		return 0x02;
+	/* FIXME: Type1 half-rate and type3 half-rate */
+	}
+	return 0;
+#else
+	/* hard-code EFR for now, since tch_mode is not correct at this
+	 * point in time */
+	return 0x01;
+#endif
+}
+
 /* ip.access specific RSL extensions */
 int rsl_ipacc_bind(struct gsm_lchan *lchan)
 {
 	struct msgb *msg = rsl_msgb_alloc();
 	struct abis_rsl_dchan_hdr *dh;
+	u_int8_t speech_mode_s;
 
 	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
 	init_dchan_hdr(dh, RSL_MT_IPAC_BIND);
 	dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
 	dh->chan_nr = lchan2chan_nr(lchan);
 
+	speech_mode_s = ipa_smod_s_for_tch_mode(lchan->tch_mode);
+	/* 0x1- == receive-only, 0x-1 == EFR codec */
+	msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, 0x10 | speech_mode_s);
+
+	DEBUGPC(DRSL, "channel=%s chan_nr=0x%02x IPAC_BIND\n",
+		gsm_ts_name(lchan->ts), dh->chan_nr);
+
 	msg->trx = lchan->ts->trx;
 
 	return abis_rsl_sendmsg(msg);
@@ -1197,12 +1292,20 @@
 	struct msgb *msg = rsl_msgb_alloc();
 	struct abis_rsl_dchan_hdr *dh;
 	u_int8_t *att_f8, *att_ip, *att_port;
+	u_int8_t speech_mode_s;
+	struct in_addr ia;
 
 	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
 	init_dchan_hdr(dh, RSL_MT_IPAC_CONNECT);
 	dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS;
 	dh->chan_nr = lchan2chan_nr(lchan);
 
+	ia.s_addr = htonl(ip);
+	DEBUGP(DRSL, "IPAC_CONNECT channel=%s chan_nr=0x%02x "
+		"IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d\n",
+		gsm_ts_name(lchan->ts), dh->chan_nr,
+		inet_ntoa(ia), port, rtp_payload2, conn_id);
+
 	att_f8 = msgb_put(msg, sizeof(conn_id)+1);
 	att_f8[0] = RSL_IE_IPAC_CONN_ID;
 	att_f8[1] = conn_id >> 8;
@@ -1221,8 +1324,12 @@
 	att_port[1] = port >> 8;
 	att_port[2] = port & 0xff;
 
-	msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, 1);	/* F4 01 */
-	msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); /* FC 7F */
+	speech_mode_s = ipa_smod_s_for_tch_mode(lchan->tch_mode);
+	/* 0x0- == both directions, 0x-1 == EFR codec */
+	msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, 0x00 | speech_mode_s);
+	if (rtp_payload2)
+		msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2);
+	
 	msg->trx = lchan->ts->trx;
 
 	return abis_rsl_sendmsg(msg);
@@ -1243,7 +1350,6 @@
 	rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
 	if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) ||
 	    !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) ||
-	    !TLVP_PRESENT(&tv, RSL_IE_IPAC_RTP_PAYLOAD2) ||
 	    !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) {
 		DEBUGPC(DRSL, "mandatory IE missing");
 		return -EINVAL;
@@ -1252,15 +1358,20 @@
 	port = *((u_int16_t *) TLVP_VAL(&tv, RSL_IE_IPAC_LOCAL_PORT));
 	attr_f8 = *((u_int16_t *) TLVP_VAL(&tv, 0xf8));
 
-	DEBUGPC(DRSL, "IP=%s PORT=%d RTP_PAYLOAD2=%d CONN_ID=%d",
-		inet_ntoa(ip), ntohs(port), *TLVP_VAL(&tv, 0xfc),
-		ntohs(attr_f8));
+	DEBUGPC(DRSL, "IP=%s PORT=%d CONN_ID=%d ",
+		inet_ntoa(ip), ntohs(port), ntohs(attr_f8));
+
+	if (TLVP_PRESENT(&tv, RSL_IE_IPAC_RTP_PAYLOAD2)) {
+		ts->abis_ip.rtp_payload2 = 
+				*TLVP_VAL(&tv, RSL_IE_IPAC_RTP_PAYLOAD2);
+		DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ",
+			ts->abis_ip.rtp_payload2);
+	}
 
 	/* update our local information about this TS */
 	ts->abis_ip.bound_ip = ntohl(ip.s_addr);
 	ts->abis_ip.bound_port = ntohs(port);
 	ts->abis_ip.conn_id = ntohs(attr_f8);
-	ts->abis_ip.rtp_payload2 = *TLVP_VAL(&tv, RSL_IE_IPAC_RTP_PAYLOAD2);
 
 	dispatch_signal(SS_ABISIP, S_ABISIP_BIND_ACK, msg->lchan);
 
@@ -1273,12 +1384,12 @@
 	struct tlv_parsed tv;
 
 	rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));
-	if (!TLVP_PRESENT(&tv, RSL_IE_CAUSE)) {
-		DEBUGPC(DRSL, "mandatory IE missing! ");
-		return -EINVAL;
-	}
 
-	DEBUGPC(DRSL, "cause=0x%02x ", *TLVP_VAL(&tv, RSL_IE_CAUSE));
+	if (TLVP_PRESENT(&tv, RSL_IE_CAUSE))
+		print_rsl_cause(TLVP_VAL(&tv, RSL_IE_CAUSE),
+				TLVP_LEN(&tv, RSL_IE_CAUSE));
+
+	dispatch_signal(SS_ABISIP, S_ABISIP_DISC_IND, msg->lchan);
 
 	return 0;
 }
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 688ee9f..ee8bd1f 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -56,12 +56,24 @@
 static int MCC = 1;
 static int MNC = 1;
 static int LAC = 1;
+static int TSC = HARDCODED_TSC;
+static int BSIC = HARDCODED_BSIC;
 static int ARFCN = HARDCODED_ARFCN;
 static int cardnr = 0;
 static int release_l2 = 0;
 static enum gsm_bts_type BTS_TYPE = GSM_BTS_TYPE_BS11;
 static enum gsm_band BAND = GSM_BAND_900;
 static const char *database_name = "hlr.sqlite3";
+extern int ipacc_rtp_direct;
+
+struct nano_bts_id {
+	struct llist_head entry;
+	int site_id;
+	int bts_id;
+};
+
+static LLIST_HEAD(nanobts_ids);
+
 
 /* The following definitions are for OM and NM packets that we cannot yet
  * generate by code but we just pass on */
@@ -219,26 +231,27 @@
 unsigned char msg_3[] = 
 {
 	NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, 
-		0xD0, 0x00,
-		0x64, 0x00,
-		0x67, 0x00,
-		0x68, 0x00,
-		0x6A, 0x00,
-		0x6C, 0x00,
-		0x6D, 0x00,
-		0x6F, 0x08,
-		0x70, 0x08, 0x01,
+		0xD0, 0x00,		/* enableDelayPowerBudgetHO */
+		0x64, 0x00,		/* enableDistanceHO */
+		0x67, 0x00,		/* enableInternalInterCellHandover */
+		0x68, 0x00,		/* enableInternalInterCellHandover */
+		0x6A, 0x00,		/* enablePowerBudgetHO */
+		0x6C, 0x00,		/* enableRXLEVHO */
+		0x6D, 0x00,		/* enableRXQUALHO */
+		0x6F, 0x08,		/* hoAveragingDistance */
+		0x70, 0x08, 0x01,	/* hoAveragingLev */
 		0x71, 0x10, 0x10, 0x10,
-		0x72, 0x08, 0x02,
-		0x73, 0x0A,
-		0x74, 0x05,
-		0x75, 0x06,
-		0x76, 0x06,
-		0x78, 0x14,
-		0x79, 0x14,
-		0x7A, 0x14,
-		0x7D, 0x06,
-		0x92, 0x03, 0x20, 0x01, 0x00,
+		0x72, 0x08, 0x02,	/* hoAveragingQual */
+		0x73, 0x0A,		/* hoLowerThresholdLevDL */
+		0x74, 0x05,		/* hoLowerThresholdLevUL */
+		0x75, 0x06,		/* hoLowerThresholdQualDL */
+		0x76, 0x06,		/* hoLowerThresholdQualUL */
+		0x78, 0x14,		/* hoThresholdLevDLintra */
+		0x79, 0x14,		/* hoThresholdLevULintra */
+		0x7A, 0x14,		/* hoThresholdMsRangeMax */
+		0x7D, 0x06,		/* nCell */
+		NM_ATT_BS11_TIMER_HO_REQUEST, 0x03,
+		0x20, 0x01, 0x00,
 		0x45, 0x01, 0x00,
 		0x48, 0x01, 0x00,
 		0x5A, 0x01, 0x00,
@@ -291,22 +304,22 @@
 	NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, 
 		NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00,
 		NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00,
-		0x7E, 0x04, 0x01,
-		0x7F, 0x04, 0x02,
-		0x80, 0x0F,
-		0x81, 0x0A,
-		0x82, 0x05,
-		0x83, 0x05,
-		0x84, 0x0C, 
-		0x85, 0x14, 
-		0x86, 0x0F, 
-		0x87, 0x04,
-		0x88, 0x04,
-		0x89, 0x02,
-		0x8A, 0x02,
-		0x8B, 0x02,
-		0x8C, 0x01,
-		0x8D, 0x40,
+		0x7E, 0x04, 0x01,	/* pcAveragingLev */
+		0x7F, 0x04, 0x02,	/* pcAveragingQual */
+		0x80, 0x0F,		/* pcLowerThresholdLevDL */
+		0x81, 0x0A,		/* pcLowerThresholdLevUL */
+		0x82, 0x05,		/* pcLowerThresholdQualDL */
+		0x83, 0x05,		/* pcLowerThresholdQualUL */
+		0x84, 0x0C, 		/* pcRLFThreshold */
+		0x85, 0x14, 		/* pcUpperThresholdLevDL */
+		0x86, 0x0F, 		/* pcUpperThresholdLevUL */
+		0x87, 0x04,		/* pcUpperThresholdQualDL */
+		0x88, 0x04,		/* pcUpperThresholdQualUL */
+		0x89, 0x02,		/* powerConfirm */
+		0x8A, 0x02,		/* powerConfirmInterval */
+		0x8B, 0x02,		/* powerIncrStepSize */
+		0x8C, 0x01,		/* powerRedStepSize */
+		0x8D, 0x40,		/* radioLinkTimeoutBs */
 		0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl
 };
 
@@ -883,13 +896,18 @@
 	/* patch BSIC */
 	bs11_attr_bts[1] = bts->bsic;
 	nanobts_attr_bts[sizeof(nanobts_attr_bts)-1] = bts->bsic;
+
+	/* patch TSC */
+	si4[15] &= ~0xe0;
+	si4[15] |= (bts->tsc & 7) << 5;
 }
 
 
 static void bootstrap_rsl(struct gsm_bts_trx *trx)
 {
 	fprintf(stdout, "bootstrapping RSL for BTS/TRX (%u/%u) "
-		"using MCC=%u MNC=%u\n", trx->nr, trx->bts->nr, MCC, MNC);
+		"using MCC=%u MNC=%u BSIC=%u TSC=%u\n",
+		trx->nr, trx->bts->nr, MCC, MNC, BSIC, TSC);
 	set_system_infos(trx);
 }
 
@@ -963,8 +981,6 @@
 
 static int bootstrap_network(void)
 {
-	struct gsm_bts *bts;
-
 	switch(BTS_TYPE) {
 	case GSM_BTS_TYPE_NANOBTS_1800:
 		if (ARFCN < 512 || ARFCN > 885) {
@@ -993,9 +1009,6 @@
 	gsmnet->name_long = "OpenBSC";
 	gsmnet->name_short = "OpenBSC";
 
-	bts = gsm_bts_alloc(gsmnet, BTS_TYPE, HARDCODED_TSC, HARDCODED_BSIC);
-	bootstrap_bts(bts);
-
 	if (db_init(database_name)) {
 		printf("DB: Failed to init database. Please check the option settings.\n");
 		return -1;
@@ -1014,17 +1027,27 @@
 
 	/* E1 mISDN input setup */
 	if (BTS_TYPE == GSM_BTS_TYPE_BS11) {
+		struct gsm_bts *bts = gsm_bts_alloc(gsmnet, BTS_TYPE, TSC, BSIC);
+		bootstrap_bts(bts);
+
 		gsmnet->num_bts = 1;
 		return e1_config(bts, cardnr, release_l2);
 	} else {
-		/* FIXME: do this dynamic */
-		bts->ip_access.site_id = 1801;
-		bts->ip_access.bts_id = 0;
+		struct nano_bts_id *bts_id;
+		struct gsm_bts *bts;
 
-		bts = gsm_bts_alloc(gsmnet, BTS_TYPE, HARDCODED_TSC, HARDCODED_BSIC);
-		bootstrap_bts(bts);
-		bts->ip_access.site_id = 1800;
-		bts->ip_access.bts_id = 0;
+		if (llist_empty(&nanobts_ids)) {
+			fprintf(stderr, "You need to specify -i DEVICE_1 -i DEVICE_2 for nanoBTS.\n");
+			return -EINVAL;
+		}
+
+		llist_for_each_entry(bts_id, &nanobts_ids, entry) {
+			bts = gsm_bts_alloc(gsmnet, BTS_TYPE, TSC, BSIC);
+			bootstrap_bts(bts);
+			bts->ip_access.site_id = bts_id->site_id;
+			bts->ip_access.bts_id = 0;
+		}
+
 		return ipaccess_setup(gsmnet);
 	}
 }
@@ -1061,6 +1084,7 @@
 	printf("  -r --reject-cause number The reject cause for LOCATION UPDATING REJECT.\n");
 	printf("  -p --pcap file  The filename of the pcap file\n");
 	printf("  -t --bts-type type The BTS type (bs11, nanobts900, nanobts1800)\n");
+	printf("  -i --bts-id=NUMBER The known nanoBTS device numbers. Can be specified multiple times.\n");
 	printf("  -C --cardnr number  For bs11 select E1 card number other than 0\n");
 	printf("  -R --release-l2 Releases mISDN layer 2 after exit, to unload driver.\n");
 	printf("  -h --help this text\n");
@@ -1087,10 +1111,14 @@
 			{"release-l2", 0, 0, 'R'},
 			{"timestamp", 0, 0, 'T'},
 			{"band", 0, 0, 'b'},
+			{"bts-id", 1, 0, 'i'},
+			{"tsc", 1, 0, 'S'},
+			{"bsic", 1, 0, 'B'},
+			{"rtp-proxy", 0, 0, 'P'},
 			{0, 0, 0, 0}
 		};
 
-		c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:Tb:",
+		c = getopt_long(argc, argv, "hc:n:d:sar:p:f:t:C:RL:l:Tb:i:S:B:P",
 				long_options, &option_index);
 		if (c == -1)
 			break;
@@ -1145,6 +1173,26 @@
 		case 'b':
 			BAND = gsm_band_parse(atoi(optarg));
 			break;
+		case 'i': {
+			struct nano_bts_id *bts_id = talloc_zero(tall_bsc_ctx, struct nano_bts_id);
+			if (!bts_id) {
+				fprintf(stderr, "Failed to allocate bts id\n");
+				exit(-1);
+			}
+
+			bts_id->site_id = atoi(optarg);
+			llist_add(&bts_id->entry, &nanobts_ids);
+			break;
+		case 'S':
+			TSC = atoi(optarg);
+			break;
+		case 'B':
+			BSIC = atoi(optarg);
+			break;
+		case 'P':
+			ipacc_rtp_direct = 0;
+			break;
+		}
 		default:
 			/* ignore */
 			break;
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 543f44c..fc2950a 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -158,12 +158,13 @@
 	return 0;
 }
 
-struct gsm_subscriber* db_create_subscriber(char *imsi) {
+struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
+{
 	dbi_result result;
 	struct gsm_subscriber* subscr;
 
 	/* Is this subscriber known in the db? */
-	subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi); 
+	subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); 
 	if (subscr) {
 		result = dbi_conn_queryf(conn,
                          "UPDATE Subscriber set updated = datetime('now') "
@@ -189,6 +190,7 @@
 	if (result==NULL) {
 		printf("DB: Failed to create Subscriber by IMSI.\n");
 	}
+	subscr->net = net;
 	subscr->id = dbi_conn_sequence_last(conn, NULL);
 	strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1);
 	dbi_result_free(result);
@@ -196,7 +198,10 @@
 	return subscr;
 }
 
-struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, const char *id) {
+struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
+					 enum gsm_subscriber_field field,
+					 const char *id)
+{
 	dbi_result result;
 	const char *string;
 	char *quoted;
@@ -246,6 +251,7 @@
 	}
 
 	subscr = subscr_alloc();
+	subscr->net = net;
 	subscr->id = dbi_result_get_ulonglong(result, "id");
 	string = dbi_result_get_string(result, "imsi");
 	if (string)
@@ -459,7 +465,7 @@
 }
 
 /* retrieve the next unsent SMS with ID >= min_id */
-struct gsm_sms *db_sms_get_unsent(int min_id)
+struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
 {
 	dbi_result result;
 	struct gsm_sms *sms = malloc(sizeof(*sms));
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index 2d0c134..7531755 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -370,10 +370,6 @@
 	if (ts->type != E1INP_TS_TYPE_SIGN)
 		return NULL;
 
-	if (!tall_sigl_ctx)
-		tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						   "e1inp_sign_link");
-
 	link = talloc_zero(tall_sigl_ctx, struct e1inp_sign_link);
 	if (!link)
 		return NULL;
@@ -505,3 +501,9 @@
 	
 	return 0;
 }
+
+static __attribute__((constructor)) void on_dso_load_e1_inp(void)
+{
+	tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1,
+					   "e1inp_sign_link");
+}
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 9e0427e..2d3a634 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -44,7 +44,9 @@
 #include <openbsc/signal.h>
 #include <openbsc/trau_frame.h>
 #include <openbsc/trau_mux.h>
+#include <openbsc/rtp_proxy.h>
 #include <openbsc/talloc.h>
+#include <openbsc/transaction.h>
 
 #define GSM48_ALLOC_SIZE	1024
 #define GSM48_ALLOC_HEADROOM	128
@@ -54,7 +56,10 @@
 #define GSM_MAX_USERUSER       128
 
 static void *tall_locop_ctx;
-static void *tall_trans_ctx;
+
+/* should ip.access BTS use direct RTP streams between each other (1),
+ * or should OpenBSC always act as RTP relay/proxy in between (0) ? */
+int ipacc_rtp_direct = 1;
 
 static const struct tlv_definition rsl_att_tlvdef = {
 	.def = {
@@ -292,7 +297,6 @@
 static int gsm48_tx_simple(struct gsm_lchan *lchan,
 			   u_int8_t pdisc, u_int8_t msg_type);
 static void schedule_reject(struct gsm_lchan *lchan);
-void free_trans(struct gsm_trans *trans);
 
 struct gsm_lai {
 	u_int16_t mcc;
@@ -351,9 +355,6 @@
 	use_lchan(lchan);
 	release_loc_updating_req(lchan);
 
-	if (!tall_locop_ctx)
-		tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						    "loc_updating_oper");
 	lchan->loc_operation = talloc_zero(tall_locop_ctx,
 					   struct gsm_loc_updating_operation);
 }
@@ -392,23 +393,17 @@
 	release_loc_updating_req(lchan);
 
 	/* Free all transactions that are associated with the released lchan */
+	/* FIXME: this is not neccessarily the right thing to do, we should
+	 * only set trans->lchan to NULL and wait for another lchan to be
+	 * established to the same MM entity (phone/subscriber) */
 	llist_for_each_entry_safe(trans, temp, &lchan->ts->trx->bts->network->trans_list, entry) {
 		if (trans->lchan == lchan)
-			free_trans(trans);
+			trans_free(trans);
 	}
 
 	return 0;
 }
 
-/*
- * This will be ran by the linker when loading the DSO. We use it to
- * do system initialization, e.g. registration of signal handlers.
- */
-static __attribute__((constructor)) void on_dso_load_0408(void)
-{
-	register_signal_handler(SS_LCHAN, gsm0408_handle_lchan_signal, NULL);
-}
-
 static void to_bcd(u_int8_t *bcd, u_int16_t val)
 {
 	bcd[2] = val % 10;
@@ -981,10 +976,18 @@
 				   "GSM 04.08");
 }
 
-int gsm48_sendmsg(struct msgb *msg)
+int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans)
 {
+	struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
+
+	/* if we get passed a transaction reference, do some common
+	 * work that the caller no longer has to do */
+	if (trans) {
+		gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
+		msg->lchan = trans->lchan;
+	}
+
 	if (msg->lchan) {
-		struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
 		msg->trx = msg->lchan->ts->trx;
 
 		if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC)
@@ -1020,7 +1023,7 @@
 
 	DEBUGP(DMM, "-> LOCATION UPDATING REJECT on channel: %d\n", lchan->nr);
 	
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, NULL);
 }
 
 /* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */
@@ -1048,7 +1051,7 @@
 
 	DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
 
-	ret = gsm48_sendmsg(msg);
+	ret = gsm48_sendmsg(msg, NULL);
 
 	ret = gsm48_tx_mm_info(lchan);
 
@@ -1119,7 +1122,7 @@
 	gh->msg_type = GSM48_MT_MM_ID_REQ;
 	gh->data[0] = id_type;
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, NULL);
 }
 
 #define MI_SIZE 32
@@ -1129,6 +1132,8 @@
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	struct gsm_lchan *lchan = msg->lchan;
+	struct gsm_bts *bts = lchan->ts->trx->bts;
+	struct gsm_network *net = bts->network;
 	u_int8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK;
 	char mi_string[MI_SIZE];
 
@@ -1139,7 +1144,7 @@
 	switch (mi_type) {
 	case GSM_MI_TYPE_IMSI:
 		if (!lchan->subscr)
-			lchan->subscr = db_create_subscriber(mi_string);
+			lchan->subscr = db_create_subscriber(net, mi_string);
 		if (lchan->loc_operation)
 			lchan->loc_operation->waiting_for_imsi = 0;
 		break;
@@ -1196,6 +1201,7 @@
 	struct gsm48_loc_upd_req *lu;
 	struct gsm_subscriber *subscr = NULL;
 	struct gsm_lchan *lchan = msg->lchan;
+	struct gsm_bts *bts = lchan->ts->trx->bts;
 	u_int8_t mi_type;
 	char mi_string[MI_SIZE];
 	int rc;
@@ -1230,7 +1236,7 @@
 		lchan->loc_operation->waiting_for_imei = 1;
 
 		/* look up subscriber based on IMSI */
-		subscr = db_create_subscriber(mi_string);
+		subscr = db_create_subscriber(bts->network, mi_string);
 		break;
 	case GSM_MI_TYPE_TMSI:
 		DEBUGPC(DMM, "\n");
@@ -1239,7 +1245,7 @@
 		lchan->loc_operation->waiting_for_imei = 1;
 
 		/* look up the subscriber based on TMSI, request IMSI if it fails */
-		subscr = subscr_get_by_tmsi(mi_string);
+		subscr = subscr_get_by_tmsi(bts->network, mi_string);
 		if (!subscr) {
 			/* send IDENTITY REQUEST message to get IMSI */
 			rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI);
@@ -1297,7 +1303,7 @@
 	cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
 	cmm->mode = mode;
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, NULL);
 }
 
 #if 0
@@ -1349,7 +1355,7 @@
 		name_len = strlen(net->name_short);
 		/* 10.5.3.5a */
 		ptr8 = (u_int8_t *) msgb_put(msg, 3);
-		ptr8[0] = GSM48_IE_NAME_LONG;
+		ptr8[0] = GSM48_IE_NAME_SHORT;
 		ptr8[1] = name_len*2 + 1;
 		ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */
 
@@ -1377,7 +1383,7 @@
 		ptr8[7] |= 0x80;
 #endif
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, NULL);
 }
 
 static int gsm48_tx_mm_serv_ack(struct gsm_lchan *lchan)
@@ -1403,7 +1409,7 @@
 	gh->data[0] = value;
 	DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, NULL);
 }
 
 
@@ -1421,6 +1427,7 @@
 	u_int8_t mi_type;
 	char mi_string[MI_SIZE];
 
+	struct gsm_bts *bts = msg->lchan->ts->trx->bts;
 	struct gsm_subscriber *subscr;
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	struct gsm48_service_request *req =
@@ -1455,7 +1462,7 @@
 	DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n",
 		req->cm_service_type, mi_type, mi_string);
 
-	subscr = subscr_get_by_tmsi(mi_string);
+	subscr = subscr_get_by_tmsi(bts->network, mi_string);
 
 	/* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */
 	if (!subscr)
@@ -1478,6 +1485,7 @@
 
 static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
 {
+	struct gsm_bts *bts = msg->lchan->ts->trx->bts;
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	struct gsm48_imsi_detach_ind *idi =
 				(struct gsm48_imsi_detach_ind *) gh->data;
@@ -1491,10 +1499,10 @@
 
 	switch (mi_type) {
 	case GSM_MI_TYPE_TMSI:
-		subscr = subscr_get_by_tmsi(mi_string);
+		subscr = subscr_get_by_tmsi(bts->network, mi_string);
 		break;
 	case GSM_MI_TYPE_IMSI:
-		subscr = subscr_get_by_imsi(mi_string);
+		subscr = subscr_get_by_imsi(bts->network, mi_string);
 		break;
 	case GSM_MI_TYPE_IMEI:
 	case GSM_MI_TYPE_IMEISV:
@@ -1574,6 +1582,7 @@
 /* Receive a PAGING RESPONSE message from the MS */
 static int gsm48_rr_rx_pag_resp(struct msgb *msg)
 {
+	struct gsm_bts *bts = msg->lchan->ts->trx->bts;
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	u_int8_t *classmark2_lv = gh->data + 1;
 	u_int8_t *mi_lv = gh->data + 2 + *classmark2_lv;
@@ -1588,10 +1597,10 @@
 		mi_type, mi_string);
 	switch (mi_type) {
 	case GSM_MI_TYPE_TMSI:
-		subscr = subscr_get_by_tmsi(mi_string);
+		subscr = subscr_get_by_tmsi(bts->network, mi_string);
 		break;
 	case GSM_MI_TYPE_IMSI:
-		subscr = subscr_get_by_imsi(mi_string);
+		subscr = subscr_get_by_imsi(bts->network, mi_string);
 		break;
 	}
 
@@ -1769,7 +1778,11 @@
 	DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n",
 		lchan->nr, lchan->type);
 
-	return gsm48_sendmsg(msg);
+	/* Send actual release request to MS */
+	gsm48_sendmsg(msg, NULL);
+
+	/* Deactivate the SACCH on the BTS side */
+	return rsl_deact_sacch(lchan);
 }
 
 /* Call Control */
@@ -1785,9 +1798,9 @@
 		return;
 
 	DEBUGP(DCC, "new state %s -> %s\n",
-		cc_state_names[trans->state], cc_state_names[state]);
+		cc_state_names[trans->cc.state], cc_state_names[state]);
 
-	trans->state = state;
+	trans->cc.state = state;
 }
 
 static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg)
@@ -1796,8 +1809,6 @@
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 	u_int8_t *cause, *call_state;
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_STATUS;
 
 	cause = msgb_put(msg, 3);
@@ -1808,7 +1819,7 @@
 	call_state = msgb_put(msg, 1);
 	call_state[0] = 0xc0 | 0x00;
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_tx_simple(struct gsm_lchan *lchan,
@@ -1822,15 +1833,15 @@
 	gh->proto_discr = pdisc;
 	gh->msg_type = msg_type;
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, NULL);
 }
 
 static void gsm48_stop_cc_timer(struct gsm_trans *trans)
 {
-	if (bsc_timer_pending(&trans->cc_timer)) {
-		DEBUGP(DCC, "stopping pending timer T%x\n", trans->Tcurrent);
-		bsc_del_timer(&trans->cc_timer);
-		trans->Tcurrent = 0;
+	if (bsc_timer_pending(&trans->cc.timer)) {
+		DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent);
+		bsc_del_timer(&trans->cc.timer);
+		trans->cc.Tcurrent = 0;
 	}
 }
  
@@ -1841,7 +1852,7 @@
 
 	if (trans)
 		if (trans->lchan)
-			DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) "
+			DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
 				"Sending '%s' to MNCC.\n",
 				trans->lchan->ts->trx->bts->nr,
 				trans->lchan->ts->trx->nr,
@@ -1879,49 +1890,23 @@
 	return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
 }
 
-void free_trans(struct gsm_trans *trans)
+/* Call Control Specific transaction release.
+ * gets called by trans_free, DO NOT CALL YOURSELF! */
+void _gsm48_cc_trans_free(struct gsm_trans *trans)
 {
-	struct gsm_bts *bts;
-
 	gsm48_stop_cc_timer(trans);
 
 	/* send release to L4, if callref still exists */
 	if (trans->callref) {
 		/* Ressource unavailable */
-		mncc_release_ind(trans->network, trans, trans->callref,
+		mncc_release_ind(trans->subscr->net, trans, trans->callref,
 				 GSM48_CAUSE_LOC_PRN_S_LU,
 				 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
-		if (trans->state != GSM_CSTATE_NULL)
-			new_cc_state(trans, GSM_CSTATE_NULL);
 	}
-
-	if (!trans->lchan && trans->subscr && trans->subscr->net) {
-		/* Stop paging on all bts' */
-		bts = NULL;
-		do {
-			bts = gsm_bts_by_lac(trans->subscr->net,
-					     trans->subscr->lac, bts);
-			if (!bts)
-				break;
-			/* Stop paging */
-			paging_request_stop(bts, trans->subscr, NULL);
-		} while (1);
-	}
-
-	if (trans->lchan) {
-		trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref);
-		put_lchan(trans->lchan);
-	}
-
-	if (trans->subscr)
-		subscr_put(trans->subscr);
-
-	if (trans->state != GSM_CSTATE_NULL)
+	if (trans->cc.state != GSM_CSTATE_NULL)
 		new_cc_state(trans, GSM_CSTATE_NULL);
-
-	llist_del(&trans->entry);
-
-	talloc_free(trans);
+	if (trans->lchan)
+		trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref);
 }
 
 static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg);
@@ -1962,7 +1947,7 @@
 				use_lchan(lchan);
 			}
 			/* send SETUP request to called party */
-			gsm48_cc_tx_setup(transt, &transt->cc_msg);
+			gsm48_cc_tx_setup(transt, &transt->cc.msg);
 			if (is_ipaccess_bts(lchan->ts->trx->bts))
 				rsl_ipacc_bind(lchan);
 			break;
@@ -1970,23 +1955,92 @@
 			DEBUGP(DCC, "Paging subscr %s expired!\n",
 				subscr->extension);
 			/* Temporarily out of order */
-			mncc_release_ind(transt->network, transt, transt->callref,
+			mncc_release_ind(transt->subscr->net, transt,
+					 transt->callref,
 					 GSM48_CAUSE_LOC_PRN_S_LU,
 					 GSM48_CC_CAUSE_DEST_OOO);
 			transt->callref = 0;
-			free_trans(transt);
+			trans_free(transt);
 			break;
 		}
 	}
 	return 0;
 }
 
+/* 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)
+{
+	struct gsm_lchan *lchan = signal_data;
+	struct gsm_bts_trx_ts *ts;
+	int rc;
+
+	if (subsys != SS_ABISIP)
+		return 0;
+
+	/* in case we use direct BTS-to-BTS RTP */
+	if (ipacc_rtp_direct)
+		return 0;
+
+	ts = lchan->ts;
+
+	switch (signal) {
+	case S_ABISIP_BIND_ACK:
+		/* the BTS has successfully bound a TCH to a local ip/port,
+		 * which means we can connect our UDP socket to it */
+		if (ts->abis_ip.rtp_socket) {
+			rtp_socket_free(ts->abis_ip.rtp_socket);
+			ts->abis_ip.rtp_socket = NULL;
+		}
+
+		ts->abis_ip.rtp_socket = rtp_socket_create();
+		if (!ts->abis_ip.rtp_socket)
+			goto out_err;
+
+		rc = rtp_socket_connect(ts->abis_ip.rtp_socket,
+				   ts->abis_ip.bound_ip,
+				   ts->abis_ip.bound_port);
+		if (rc < 0)
+			goto out_err;
+		break;
+	case S_ABISIP_DISC_IND:
+		/* the BTS tells us a RTP stream has been disconnected */
+		if (ts->abis_ip.rtp_socket) {
+			rtp_socket_free(ts->abis_ip.rtp_socket);
+			ts->abis_ip.rtp_socket = NULL;
+		}
+		break;
+	}
+
+	return 0;
+out_err:
+	/* FIXME: do something */
+	return 0;
+}
+
+/* bind rtp proxy to local IP/port and tell BTS to connect to it */
+static int ipacc_connect_proxy_bind(struct gsm_lchan *lchan)
+{
+	struct gsm_bts_trx_ts *ts = lchan->ts;
+	struct rtp_socket *rs = ts->abis_ip.rtp_socket;
+	int rc;
+
+	rc = rsl_ipacc_connect(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr),
+				ntohs(rs->rtp.sin_local.sin_port),
+				ts->abis_ip.conn_id, 
+			/* FIXME: use RTP payload of bound socket, not BTS*/
+				ts->abis_ip.rtp_payload2);
+
+	return rc;
+}
+
 /* map two ipaccess RTP streams onto each other */
 static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan)
 {
 	struct gsm_bts *bts = lchan->ts->trx->bts;
 	struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts;
 	struct gsm_bts_trx_ts *ts;
+	int rc;
 
 	DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n",
 		bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
@@ -2000,44 +2054,49 @@
 	switch (bts->type) {
 	case GSM_BTS_TYPE_NANOBTS_900:
 	case GSM_BTS_TYPE_NANOBTS_1800:
-		ts = remote_lchan->ts;
-		rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip,
-				  ts->abis_ip.bound_port,
-				  lchan->ts->abis_ip.conn_id,
-				  ts->abis_ip.rtp_payload2);
-	
-		ts = lchan->ts;
-		rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip,
-				  ts->abis_ip.bound_port,
-				  remote_lchan->ts->abis_ip.conn_id,
-				  ts->abis_ip.rtp_payload2);
+		if (!ipacc_rtp_direct) {
+			/* connect the TCH's to our RTP proxy */
+			rc = ipacc_connect_proxy_bind(lchan);
+			if (rc < 0)
+				return rc;
+			rc = ipacc_connect_proxy_bind(remote_lchan);
+
+			/* connect them with each other */
+			rtp_socket_proxy(lchan->ts->abis_ip.rtp_socket,
+					 remote_lchan->ts->abis_ip.rtp_socket);
+		} else {
+			/* directly connect TCH RTP streams to each other */
+			ts = remote_lchan->ts;
+			rc = rsl_ipacc_connect(lchan, ts->abis_ip.bound_ip,
+						ts->abis_ip.bound_port,
+						lchan->ts->abis_ip.conn_id,
+						ts->abis_ip.rtp_payload2);
+			if (rc < 0)
+				return rc;
+			ts = lchan->ts;
+			rc = rsl_ipacc_connect(remote_lchan, ts->abis_ip.bound_ip,
+						ts->abis_ip.bound_port,
+						remote_lchan->ts->abis_ip.conn_id,
+						ts->abis_ip.rtp_payload2);
+		}
 		break;
 	case GSM_BTS_TYPE_BS11:
 		trau_mux_map_lchan(lchan, remote_lchan);
 		break;
 	default:
 		DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
+		rc = -EINVAL;
 		break;
 	}
 
 	return 0;
 }
 
-static struct gsm_trans *get_trans_ref(struct gsm_network *net, u_int32_t callref)
-{
-	struct gsm_trans *trans;
-	llist_for_each_entry(trans, &net->trans_list, entry) {
-		if (trans->callref == callref)
-			return trans;
-	}
-	return NULL;
-}
-
 /* bridge channels of two transactions */
 static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
 {
-	struct gsm_trans *trans1 = get_trans_ref(net, refs[0]);
-	struct gsm_trans *trans2 = get_trans_ref(net, refs[1]);
+	struct gsm_trans *trans1 = trans_find_by_callref(net, refs[0]);
+	struct gsm_trans *trans2 = trans_find_by_callref(net, refs[1]);
 
 	if (!trans1 || !trans2)
 		return -EIO;
@@ -2055,7 +2114,7 @@
 	struct gsm_trans *trans;
 
 	/* Find callref */
-	trans = get_trans_ref(net, data->callref);
+	trans = trans_find_by_callref(net, data->callref);
 	if (!trans)
 		return -EIO;
 	if (!trans->lchan)
@@ -2073,7 +2132,7 @@
 	struct gsm_trans *trans;
 
 	/* Find callref */
-	trans = get_trans_ref(net, frame->callref);
+	trans = trans_find_by_callref(net, frame->callref);
 	if (!trans)
 		return -EIO;
 	if (!trans->lchan)
@@ -2112,7 +2171,7 @@
 	memset(&l4_rel, 0, sizeof(struct gsm_mncc));
 	l4_rel.callref = trans->callref;
 
-	switch(trans->Tcurrent) {
+	switch(trans->cc.Tcurrent) {
 	case 0x303:
 		release = 1;
 		l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
@@ -2130,21 +2189,21 @@
 		l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND;
 		break;
 	case 0x308:
-		if (!trans->T308_second) {
+		if (!trans->cc.T308_second) {
 			/* restart T308 a second time */
-			gsm48_cc_tx_release(trans, &trans->cc_msg);
-			trans->T308_second = 1;
+			gsm48_cc_tx_release(trans, &trans->cc.msg);
+			trans->cc.T308_second = 1;
 			break; /* stay in release state */
 		}
-		free_trans(trans);
+		trans_free(trans);
 		return;
 //		release = 1;
 //		l4_cause = 14;
 //		break;
 	case 0x306:
 		release = 1;
-		mo_cause = trans->cc_msg.cause.value;
-		mo_location = trans->cc_msg.cause.location;
+		mo_cause = trans->cc.msg.cause.value;
+		mo_location = trans->cc.msg.cause.location;
 		break;
 	case 0x323:
 		disconnect = 1;
@@ -2155,7 +2214,7 @@
 
 	if (release && trans->callref) {
 		/* process release towards layer 4 */
-		mncc_release_ind(trans->network, trans, trans->callref,
+		mncc_release_ind(trans->subscr->net, trans, trans->callref,
 				 l4_location, l4_cause);
 		trans->callref = 0;
 	}
@@ -2163,15 +2222,15 @@
 	if (disconnect && trans->callref) {
 		/* process disconnect towards layer 4 */
 		mncc_set_cause(&l4_rel, l4_location, l4_cause);
-		mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &l4_rel);
+		mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &l4_rel);
 	}
 
 	/* process disconnect towards mobile station */
 	if (disconnect || release) {
 		mncc_set_cause(&mo_rel, mo_location, mo_cause);
-		mo_rel.cause.diag[0] = ((trans->Tcurrent & 0xf00) >> 8) + '0';
-		mo_rel.cause.diag[1] = ((trans->Tcurrent & 0x0f0) >> 4) + '0';
-		mo_rel.cause.diag[2] = (trans->Tcurrent & 0x00f) + '0';
+		mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0';
+		mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0';
+		mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0';
 		mo_rel.cause.diag_len = 3;
 
 		if (disconnect)
@@ -2186,10 +2245,10 @@
 				 int sec, int micro)
 {
 	DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec);
-	trans->cc_timer.cb = gsm48_cc_timeout;
-	trans->cc_timer.data = trans;
-	bsc_schedule_timer(&trans->cc_timer, sec, micro);
-	trans->Tcurrent = current;
+	trans->cc.timer.cb = gsm48_cc_timeout;
+	trans->cc.timer.data = trans;
+	bsc_schedule_timer(&trans->cc.timer, sec, micro);
+	trans->cc.Tcurrent = current;
 }
 
 static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg)
@@ -2264,7 +2323,7 @@
 	new_cc_state(trans, GSM_CSTATE_INITIATED);
 
 	/* indicate setup to MNCC */
-	mncc_recvmsg(trans->network, trans, MNCC_SETUP_IND, &setup);
+	mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup);
 
 	return 0;
 }
@@ -2274,9 +2333,7 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh;
 	struct gsm_mncc *setup = arg;
-	struct gsm_trans *transt;
-	u_int16_t trans_id_mask = 0;
-	int rc, i;
+	int rc, trans_id;
 
 	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
@@ -2285,40 +2342,27 @@
 		DEBUGP(DCC, "TX Setup with assigned transaction. "
 			"This is not allowed!\n");
 		/* Temporarily out of order */
-		rc = mncc_release_ind(trans->network, trans, trans->callref,
+		rc = mncc_release_ind(trans->subscr->net, trans, trans->callref,
 				      GSM48_CAUSE_LOC_PRN_S_LU,
 				      GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
 		trans->callref = 0;
-		free_trans(trans);
+		trans_free(trans);
 		return rc;
 	}
 	
 	/* Get free transaction_id */
-	llist_for_each_entry(transt, &trans->network->trans_list, entry) {
-		/* Transaction of our lchan? */
-		if (transt->lchan == trans->lchan &&
-		    transt->transaction_id != 0xff)
-			trans_id_mask |= (1 << (transt->transaction_id >> 4));
-	}
-	/* Assign free transaction ID */
-	if ((trans_id_mask & 0x007f) == 0x7f) {
+	trans_id = trans_assign_trans_id(trans->subscr, GSM48_PDISC_CC, 0);
+	if (trans_id < 0) {
 		/* no free transaction ID */
-		rc = mncc_release_ind(trans->network, trans, trans->callref,
+		rc = mncc_release_ind(trans->subscr->net, trans, trans->callref,
 				      GSM48_CAUSE_LOC_PRN_S_LU,
 				      GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
 		trans->callref = 0;
-		free_trans(trans);
+		trans_free(trans);
 		return rc;
 	}
-	for (i = 0; i < 7; i++) {
-		if ((trans_id_mask & (1 << i)) == 0) {
-			trans->transaction_id = i << 4; /* flag = 0 */
-			break;
-		}
-	}
+	trans->transaction_id = trans_id;
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_SETUP;
 
 	gsm48_start_cc_timer(trans, 0x303, GSM48_T303);
@@ -2350,7 +2394,7 @@
 	
 	new_cc_state(trans, GSM_CSTATE_CALL_PRESENT);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg)
@@ -2394,7 +2438,8 @@
 
 	new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF);
 
-	return mncc_recvmsg(trans->network, trans, MNCC_CALL_CONF_IND, &call_conf);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_CALL_CONF_IND,
+			    &call_conf);
 }
 
 static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg)
@@ -2403,8 +2448,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_CALL_PROC;
 
 	new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC);
@@ -2419,7 +2462,7 @@
 	if (proceeding->fields & MNCC_F_PROGRESS)
 		encode_progress(msg, 0, &proceeding->progress);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg)
@@ -2457,7 +2500,8 @@
 
 	new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED);
 
-	return mncc_recvmsg(trans->network, trans, MNCC_ALERT_IND, &alerting);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_ALERT_IND,
+			    &alerting);
 }
 
 static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg)
@@ -2466,8 +2510,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_ALERTING;
 
 	/* facility */
@@ -2482,7 +2524,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED);
 	
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg)
@@ -2491,8 +2533,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_PROGRESS;
 
 	/* progress */
@@ -2501,7 +2541,7 @@
 	if (progress->fields & MNCC_F_USERUSER)
 		encode_useruser(msg, 0, &progress->useruser);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg)
@@ -2510,8 +2550,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_CONNECT;
 
 	gsm48_stop_cc_timer(trans);
@@ -2532,7 +2570,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_CONNECT_IND);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg)
@@ -2576,7 +2614,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST);
 
-	return mncc_recvmsg(trans->network, trans, MNCC_SETUP_CNF, &connect);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_CNF, &connect);
 }
 
 
@@ -2590,7 +2628,7 @@
 	
 	memset(&connect_ack, 0, sizeof(struct gsm_mncc));
 	connect_ack.callref = trans->callref;
-	return mncc_recvmsg(trans->network, trans, MNCC_SETUP_COMPL_IND,
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_COMPL_IND,
 			    &connect_ack);
 }
 
@@ -2599,13 +2637,11 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_CONNECT_ACK;
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg)
@@ -2647,7 +2683,7 @@
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
-	return mncc_recvmsg(trans->network, trans, MNCC_DISC_IND, &disc);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &disc);
 
 }
 
@@ -2667,8 +2703,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_DISCONNECT;
 
 	gsm48_stop_cc_timer(trans);
@@ -2691,11 +2725,11 @@
 		encode_useruser(msg, 0, &disc->useruser);
 
 	/* store disconnect cause for T306 expiry */
-	memcpy(&trans->cc_msg, disc, sizeof(struct gsm_mncc));
+	memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc));
 
 	new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg)
@@ -2736,19 +2770,20 @@
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
-	if (trans->state == GSM_CSTATE_RELEASE_REQ) {
+	if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) {
 		/* release collision 5.4.5 */
-		rc = mncc_recvmsg(trans->network, trans, MNCC_REL_CNF, &rel);
+		rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_CNF, &rel);
 	} else {
-		rc = gsm48_tx_simple(msg->lchan, GSM48_PDISC_CC | trans->transaction_id,
-			     GSM48_MT_CC_RELEASE_COMPL);
-		rc = mncc_recvmsg(trans->network, trans, MNCC_REL_IND, &rel);
+		rc = gsm48_tx_simple(msg->lchan,
+				     GSM48_PDISC_CC | (trans->transaction_id << 4),
+				     GSM48_MT_CC_RELEASE_COMPL);
+		rc = mncc_recvmsg(trans->subscr->net, trans, MNCC_REL_IND, &rel);
 	}
 
 	new_cc_state(trans, GSM_CSTATE_NULL);
 
 	trans->callref = 0;
-	free_trans(trans);
+	trans_free(trans);
 
 	return rc;
 }
@@ -2759,8 +2794,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_RELEASE;
 
 	trans->callref = 0;
@@ -2778,13 +2811,13 @@
 	if (rel->fields & MNCC_F_USERUSER)
 		encode_useruser(msg, 0, &rel->useruser);
 
-	trans->T308_second = 0;
-	memcpy(&trans->cc_msg, rel, sizeof(struct gsm_mncc));
+	trans->cc.T308_second = 0;
+	memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc));
 
-	if (trans->state != GSM_CSTATE_RELEASE_REQ)
+	if (trans->cc.state != GSM_CSTATE_RELEASE_REQ)
 		new_cc_state(trans, GSM_CSTATE_RELEASE_REQ);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
@@ -2826,23 +2859,23 @@
 	}
 
 	if (trans->callref) {
-		switch (trans->state) {
+		switch (trans->cc.state) {
 		case GSM_CSTATE_CALL_PRESENT:
-			rc = mncc_recvmsg(trans->network, trans,
+			rc = mncc_recvmsg(trans->subscr->net, trans,
 					  MNCC_REJ_IND, &rel);
 			break;
 		case GSM_CSTATE_RELEASE_REQ:
-			rc = mncc_recvmsg(trans->network, trans,
+			rc = mncc_recvmsg(trans->subscr->net, trans,
 					  MNCC_REL_CNF, &rel);
 			break;
 		default:
-			rc = mncc_recvmsg(trans->network, trans,
+			rc = mncc_recvmsg(trans->subscr->net, trans,
 					  MNCC_REL_IND, &rel);
 		}
 	}
 
 	trans->callref = 0;
-	free_trans(trans);
+	trans_free(trans);
 
 	return rc;
 }
@@ -2853,8 +2886,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_RELEASE_COMPL;
 
 	trans->callref = 0;
@@ -2871,9 +2902,9 @@
 	if (rel->fields & MNCC_F_USERUSER)
 		encode_useruser(msg, 0, &rel->useruser);
 
-	free_trans(trans);
+	trans_free(trans);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg)
@@ -2899,7 +2930,7 @@
 				 TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1);
 	}
 
-	return mncc_recvmsg(trans->network, trans, MNCC_FACILITY_IND, &fac);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_FACILITY_IND, &fac);
 }
 
 static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg)
@@ -2908,14 +2939,12 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_FACILITY;
 
 	/* facility */
 	encode_facility(msg, 1, &fac->facility);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg)
@@ -2924,7 +2953,7 @@
 
 	memset(&hold, 0, sizeof(struct gsm_mncc));
 	hold.callref = trans->callref;
-	return mncc_recvmsg(trans->network, trans, MNCC_HOLD_IND, &hold);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_IND, &hold);
 }
 
 static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg)
@@ -2932,11 +2961,9 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_HOLD_ACK;
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg)
@@ -2945,8 +2972,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_HOLD_REJ;
 
 	/* cause */
@@ -2955,7 +2980,7 @@
 	else
 		encode_cause(msg, 1, &default_cause);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg)
@@ -2964,7 +2989,8 @@
 
 	memset(&retrieve, 0, sizeof(struct gsm_mncc));
 	retrieve.callref = trans->callref;
-	return mncc_recvmsg(trans->network, trans, MNCC_RETRIEVE_IND, &retrieve);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_IND,
+			    &retrieve);
 }
 
 static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg)
@@ -2972,11 +2998,9 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_RETR_ACK;
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg)
@@ -2985,8 +3009,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_RETR_REJ;
 
 	/* cause */
@@ -2995,7 +3017,7 @@
 	else
 		encode_cause(msg, 1, &default_cause);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg)
@@ -3015,7 +3037,7 @@
 			      TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1);
 	}
 
-	return mncc_recvmsg(trans->network, trans, MNCC_START_DTMF_IND, &dtmf);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_IND, &dtmf);
 }
 
 static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg)
@@ -3024,15 +3046,13 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_START_DTMF_ACK;
 
 	/* keypad */
 	if (dtmf->fields & MNCC_F_KEYPAD)
 		encode_keypad(msg, dtmf->keypad);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg)
@@ -3041,8 +3061,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_START_DTMF_REJ;
 
 	/* cause */
@@ -3051,7 +3069,7 @@
 	else
 		encode_cause(msg, 1, &default_cause);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg)
@@ -3059,11 +3077,9 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK;
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg)
@@ -3073,7 +3089,7 @@
 	memset(&dtmf, 0, sizeof(struct gsm_mncc));
 	dtmf.callref = trans->callref;
 
-	return mncc_recvmsg(trans->network, trans, MNCC_STOP_DTMF_IND, &dtmf);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_STOP_DTMF_IND, &dtmf);
 }
 
 static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg)
@@ -3095,7 +3111,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY);
 
-	return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_IND, &modify);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_IND, &modify);
 }
 
 static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg)
@@ -3104,8 +3120,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_MODIFY;
 
 	gsm48_start_cc_timer(trans, 0x323, GSM48_T323);
@@ -3115,7 +3129,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg)
@@ -3139,7 +3153,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
 
-	return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_CNF, &modify);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify);
 }
 
 static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg)
@@ -3148,8 +3162,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_MODIFY_COMPL;
 
 	/* bearer capability */
@@ -3157,7 +3169,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg)
@@ -3187,7 +3199,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
 
-	return mncc_recvmsg(trans->network, trans, MNCC_MODIFY_REJ, &modify);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_REJ, &modify);
 }
 
 static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg)
@@ -3196,8 +3208,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_MODIFY_REJECT;
 
 	/* bearer capability */
@@ -3207,7 +3217,7 @@
 
 	new_cc_state(trans, GSM_CSTATE_ACTIVE);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg)
@@ -3216,14 +3226,12 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_NOTIFY;
 
 	/* notify */
 	encode_notify(msg, notify->notify);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg)
@@ -3239,7 +3247,7 @@
 	if (payload_len >= 1)
 		decode_notify(&notify.notify, gh->data);
 
-	return mncc_recvmsg(trans->network, trans, MNCC_NOTIFY_IND, &notify);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, &notify);
 }
 
 static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg)
@@ -3248,8 +3256,6 @@
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 
-	gh->proto_discr = GSM48_PDISC_CC | trans->transaction_id;
-	msg->lchan = trans->lchan;
 	gh->msg_type = GSM48_MT_CC_USER_INFO;
 
 	/* user-user */
@@ -3259,7 +3265,7 @@
 	if (user->more)
 		encode_more(msg);
 
-	return gsm48_sendmsg(msg);
+	return gsm48_sendmsg(msg, trans);
 }
 
 static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
@@ -3282,7 +3288,7 @@
 	if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA))
 		user.more = 1;
 
-	return mncc_recvmsg(trans->network, trans, MNCC_USERINFO_IND, &user);
+	return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user);
 }
 
 static int gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
@@ -3381,7 +3387,7 @@
 	rel.callref = data->callref;
 
 	/* Find callref */
-	trans = get_trans_ref(net, data->callref);
+	trans = trans_find_by_callref(net, data->callref);
 
 	/* Callref unknown */
 	if (!trans) {
@@ -3406,9 +3412,10 @@
 		}
 		/* New transaction due to setup, find subscriber */
 		if (data->called.number[0])
-			subscr = subscr_get_by_extension(data->called.number);
+			subscr = subscr_get_by_extension(net,
+							data->called.number);
 		else
-			subscr = subscr_get_by_imsi(data->imsi);
+			subscr = subscr_get_by_imsi(net, data->imsi);
 		/* If subscriber is not found */
 		if (!subscr) {
 			DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
@@ -3433,7 +3440,8 @@
 						GSM48_CC_CAUSE_DEST_OOO);
 		}
 		/* Create transaction */
-		if (!(trans = talloc_zero(tall_trans_ctx, struct gsm_trans))) {
+		trans = trans_alloc(subscr, GSM48_PDISC_CC, 0xff, data->callref);
+		if (!trans) {
 			DEBUGP(DCC, "No memory for trans.\n");
 			subscr_put(subscr);
 			/* Ressource unavailable */
@@ -3442,12 +3450,6 @@
 					 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
 			return -ENOMEM;
 		}
-		trans->callref = data->callref;
-		trans->network = net;
-		trans->transaction_id = 0xff; /* unassigned */
-		llist_add_tail(&trans->entry, &net->trans_list);
-		/* Assign subscriber to transaction */
-		trans->subscr = subscr;
 		/* Find lchan */
 		for (i = 0; i < net->num_bts; i++) {
 			bts = gsm_bts_num(net, i);
@@ -3483,7 +3485,7 @@
 				return 0;
 			}
 			/* store setup informations until paging was successfull */
-			memcpy(&trans->cc_msg, data, sizeof(struct gsm_mncc));
+			memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc));
 			/* start paging subscriber on all BTS with her location */
 			subscr->net = net;
 			bts = NULL;
@@ -3521,7 +3523,7 @@
 		else
 			rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
 		trans->callref = 0;
-		free_trans(trans);
+		trans_free(trans);
 		return rc;
 	}
 
@@ -3530,13 +3532,13 @@
 		lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
 		trans->transaction_id,
 		(lchan->subscr)?(lchan->subscr->extension):"-",
-		get_mncc_name(msg_type), trans->state,
-		cc_state_names[trans->state]);
+		get_mncc_name(msg_type), trans->cc.state,
+		cc_state_names[trans->cc.state]);
 
 	/* Find function for current state and message */
 	for (i = 0; i < DOWNSLLEN; i++)
 		if ((msg_type == downstatelist[i].type)
-		 && ((1 << trans->state) & downstatelist[i].states))
+		 && ((1 << trans->cc.state) & downstatelist[i].states))
 			break;
 	if (i == DOWNSLLEN) {
 		DEBUGP(DCC, "Message unhandled at this state.\n");
@@ -3607,10 +3609,9 @@
 {
 	struct gsm48_hdr *gh = msgb_l3(msg);
 	u_int8_t msg_type = gh->msg_type & 0xbf;
-	u_int8_t transaction_id = (gh->proto_discr & 0xf0) ^ 0x80; /* flip */
+	u_int8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */
 	struct gsm_lchan *lchan = msg->lchan;
-	struct gsm_trans *trans = NULL, *transt;
-	struct gsm_network *net = lchan->ts->trx->bts->network;
+	struct gsm_trans *trans = NULL;
 	int i, rc = 0;
 
 	if (msg_type & 0x80) {
@@ -3619,50 +3620,38 @@
 	}
 	
 	/* Find transaction */
-	llist_for_each_entry(transt, &net->trans_list, entry) {
-		/* Transaction of our lchan? */
-		if (transt->lchan == lchan
-			&& transt->transaction_id == transaction_id) {
-			trans = transt;
-		}
-	}
-	
-	DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) "
+	trans = trans_find_by_id(lchan, transaction_id);
+
+	DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
 		"Received '%s' from MS in state %d (%s)\n",
 		lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
 		transaction_id, (lchan->subscr)?(lchan->subscr->extension):"-",
-		cc_msg_names[msg_type], trans?(trans->state):0,
-		cc_state_names[trans?(trans->state):0]);
+		cc_msg_names[msg_type], trans?(trans->cc.state):0,
+		cc_state_names[trans?(trans->cc.state):0]);
 
 	/* Create transaction */
 	if (!trans) {
-		DEBUGP(DCC, "Unknown transaction ID %02x, "
+		DEBUGP(DCC, "Unknown transaction ID %x, "
 			"creating new trans.\n", transaction_id);
 		/* Create transaction */
-		if (!(trans = talloc_zero(tall_trans_ctx, struct gsm_trans))) {
+		trans = trans_alloc(lchan->subscr, GSM48_PDISC_CC,
+				    transaction_id, new_callref++);
+		if (!trans) {
 			DEBUGP(DCC, "No memory for trans.\n");
 			rc = gsm48_tx_simple(msg->lchan,
-					     GSM48_PDISC_CC | transaction_id,
+					     GSM48_PDISC_CC | (transaction_id << 4),
 					     GSM48_MT_CC_RELEASE_COMPL);
 			return -ENOMEM;
 		}
-		llist_add_tail(&trans->entry, &net->trans_list);
 		/* Assign transaction */
-		trans->callref = new_callref++;
-		trans->network = net;
-		trans->transaction_id = transaction_id;
 		trans->lchan = lchan;
 		use_lchan(lchan);
-		if (lchan->subscr) {
-			trans->subscr = lchan->subscr;
-			subscr_get(trans->subscr);
-		}
 	}
 
 	/* find function for current state and message */
 	for (i = 0; i < DATASLLEN; i++)
 		if ((msg_type == datastatelist[i].type)
-		 && ((1 << trans->state) & datastatelist[i].states))
+		 && ((1 << trans->cc.state) & datastatelist[i].states))
 			break;
 	if (i == DATASLLEN) {
 		DEBUGP(DCC, "Message unhandled at this state.\n");
@@ -3819,3 +3808,15 @@
 
 	return work;
 }
+
+/*
+ * This will be ran by the linker when loading the DSO. We use it to
+ * do system initialization, e.g. registration of signal handlers.
+ */
+static __attribute__((constructor)) void on_dso_load_0408(void)
+{
+	tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 1,
+					    "loc_updating_oper");
+	register_signal_handler(SS_LCHAN, gsm0408_handle_lchan_signal, NULL);
+	register_signal_handler(SS_ABISIP, handle_abisip_signal, NULL);
+}
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 1b622b1..9218783 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -154,6 +154,7 @@
 /* process an incoming TPDU (called from RP-DATA) */
 static int gsm340_rx_tpdu(struct msgb *msg)
 {
+	struct gsm_bts *bts = msg->lchan->ts->trx->bts;
 	u_int8_t *smsp = msgb_sms(msg);
 	struct sms_submit *sms;
 	struct gsm_sms *gsms;
@@ -161,19 +162,11 @@
 	u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
 	int rc = 0;
 
-	if (!tall_sms_ctx)
-		tall_sms_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						  "sms_submit");
-
 	sms = talloc(tall_sms_ctx, struct sms_submit);
 	if (!sms)
 		return -ENOMEM;
 	memset(sms, 0, sizeof(*sms));
 
-	if (!tall_gsms_ctx)
-		tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						   "sms");
-
 	gsms = talloc(tall_gsms_ctx, struct gsm_sms);
 	if (!gsms) {
 		talloc_free(sms);
@@ -257,7 +250,7 @@
 	/* FIXME: sender refcount */
 
 	/* determine gsms->receiver based on dialled number */
-	gsms->receiver = subscr_get_by_extension(sms->dest_addr);
+	gsms->receiver = subscr_get_by_extension(bts->network, sms->dest_addr);
 	if (!gsms->receiver) {
 		rc = 1; /* cause 1: unknown subscriber */
 		goto out;
@@ -512,3 +505,9 @@
 
 	return gsm0411_sendmsg(msg);
 }
+
+static __attribute__((constructor)) void on_dso_load_sms(void)
+{
+	tall_sms_ctx = talloc_named_const(tall_bsc_ctx, 1, "sms_submit");
+	tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 1, "sms");
+}
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
index a323d4e..7480156 100644
--- a/openbsc/src/gsm_subscriber.c
+++ b/openbsc/src/gsm_subscriber.c
@@ -103,10 +103,6 @@
 {
 	struct gsm_subscriber *s;
 
-	if (!tall_subscr_ctx)
-		tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						     "subscriber");
-
 	s = talloc(tall_subscr_ctx, struct gsm_subscriber);
 	if (!s)
 		return NULL;
@@ -126,7 +122,8 @@
 	talloc_free(subscr);
 }
 
-struct gsm_subscriber *subscr_get_by_tmsi(const char *tmsi)
+struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net,
+					  const char *tmsi)
 {
 	struct gsm_subscriber *subscr;
 
@@ -136,10 +133,11 @@
 			return subscr_get(subscr);
 	}
 
-	return db_get_subscriber(GSM_SUBSCRIBER_TMSI, tmsi);
+	return db_get_subscriber(net, GSM_SUBSCRIBER_TMSI, tmsi);
 }
 
-struct gsm_subscriber *subscr_get_by_imsi(const char *imsi)
+struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net,
+					  const char *imsi)
 {
 	struct gsm_subscriber *subscr;
 
@@ -148,10 +146,11 @@
 			return subscr_get(subscr);
 	}
 
-	return db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi);
+	return db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi);
 }
 
-struct gsm_subscriber *subscr_get_by_extension(const char *ext)
+struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
+					       const char *ext)
 {
 	struct gsm_subscriber *subscr;
 
@@ -160,7 +159,7 @@
 			return subscr_get(subscr);
 	}
 
-	return db_get_subscriber(GSM_SUBSCRIBER_EXTENSION, ext);
+	return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext);
 }
 
 int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
@@ -168,6 +167,7 @@
 	/* FIXME: Migrate pending requests from one BSC to another */
 	switch (reason) {
 	case GSM_SUBSCRIBER_UPDATE_ATTACHED:
+		s->net = bts->network;
 		/* Indicate "attached to LAC" */
 		s->lac = bts->location_area_code;
 		break;
@@ -209,10 +209,6 @@
 {
 	struct subscr_request *request;
 
-	if (!tall_sub_req_ctx)
-		tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						      "subscr_request");
-
 	request = talloc(tall_sub_req_ctx, struct subscr_request);
 	if (!request) {
 		if (cbfn)
@@ -269,3 +265,11 @@
 		subscr_send_paging_request(lchan->subscr);
 }
 
+
+static __attribute__((constructor)) void on_dso_load_subscr(void)
+{
+	tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 1, "subscriber");
+
+	tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 1,
+						      "subscr_request");
+}
diff --git a/openbsc/src/mncc.c b/openbsc/src/mncc.c
index b2dab07..8cd62f6 100644
--- a/openbsc/src/mncc.c
+++ b/openbsc/src/mncc.c
@@ -140,9 +140,6 @@
 	if (call->remote_ref)
 		return 0;
 	
-	if (!tall_call_ctx)
-		tall_call_ctx = talloc_named_const(tall_bsc_ctx, 1,
-							   "gsm_call");
 	/* create remote call */
 	if (!(remote = talloc(tall_call_ctx, struct gsm_call))) {
 		memset(&mncc, 0, sizeof(struct gsm_mncc));
@@ -306,9 +303,6 @@
 	if (!call) {
 		if (msg_type != MNCC_SETUP_IND)
 			return 0; /* drop */
-		if (!tall_call_ctx)
-			tall_call_ctx = talloc_named_const(tall_bsc_ctx, 1,
-							   "gsm_call");
 		/* create call */
 		if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) {
 			struct gsm_mncc rel;
@@ -395,3 +389,8 @@
 
 	return rc;
 }
+
+static __attribute__((constructor)) void on_dso_load_trau_mncc(void)
+{
+	tall_call_ctx = talloc_named_const(tall_bsc_ctx, 1, "gsm_call");
+}
diff --git a/openbsc/src/msgb.c b/openbsc/src/msgb.c
index ae13346..52edf2d 100644
--- a/openbsc/src/msgb.c
+++ b/openbsc/src/msgb.c
@@ -33,9 +33,6 @@
 {
 	struct msgb *msg;
 
-	if (!tall_msgb_ctx)
-		tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 1, "msgb");
-
 	msg = _talloc_zero(tall_msgb_ctx, sizeof(*msg) + size, name);
 
 	if (!msg)
@@ -76,3 +73,8 @@
 	
 	return llist_entry(lh, struct msgb, list);
 }
+
+static __attribute__((constructor)) void on_dso_load_trau_msgb(void)
+{
+	tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 1, "msgb");
+}
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 0703e93..b63a717 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -219,9 +219,6 @@
 	struct gsm_bts_paging_state *bts_entry = &bts->paging;
 	struct gsm_paging_request *req;
 
-	if (!tall_paging_ctx)
-		tall_paging_ctx = talloc_named_const(NULL, 1, "paging_request");
-
 	if (paging_pending_request(bts_entry, subscr)) {
 		DEBUGP(DPAG, "Paging request already pending\n");
 		return;
@@ -310,3 +307,8 @@
 {
 	bts->paging.available_slots = free_slots;
 }
+
+static __attribute__((constructor)) void on_dso_load_paging(void)
+{
+	tall_paging_ctx = talloc_named_const(NULL, 1, "paging_request");
+}
diff --git a/openbsc/src/rtp_proxy.c b/openbsc/src/rtp_proxy.c
new file mode 100644
index 0000000..61fc385
--- /dev/null
+++ b/openbsc/src/rtp_proxy.c
@@ -0,0 +1,446 @@
+/* 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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openbsc/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/msgb.h>
+#include <openbsc/select.h>
+#include <openbsc/debug.h>
+#include <openbsc/rtp_proxy.h>
+
+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
+
+/* iterate over all chunks in one RTCP message, kook 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;
+
+	/* 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 (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 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;
+	/* FIXME: other cases */
+	}
+
+	return rc;
+
+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) {
+		perror("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;
+}
+
+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/openbsc/src/select.c b/openbsc/src/select.c
index 11b7e6b..7f45426 100644
--- a/openbsc/src/select.c
+++ b/openbsc/src/select.c
@@ -25,6 +25,7 @@
 
 static int maxfd = 0;
 static LLIST_HEAD(bsc_fds);
+static int unregistered_count;
 
 int bsc_register_fd(struct bsc_fd *fd)
 {
@@ -50,6 +51,7 @@
 
 void bsc_unregister_fd(struct bsc_fd *fd)
 {
+	unregistered_count++;
 	llist_del(&fd->list);
 }
 
@@ -86,6 +88,8 @@
 	bsc_update_timers();
 
 	/* call registered callback functions */
+restart:
+	unregistered_count = 0;
 	llist_for_each_entry_safe(ufd, tmp, &bsc_fds, list) {
 		int flags = 0;
 
@@ -102,6 +106,11 @@
 			work = 1;
 			ufd->cb(ufd, flags);
 		}
+		/* ugly, ugly hack. If more than one filedescriptors were
+		 * unregistered, they might have been consecutive and
+		 * llist_for_each_entry_safe() is no longer safe */
+		if (unregistered_count > 1)
+			goto restart;
 	}
 	return work;
 }
diff --git a/openbsc/src/signal.c b/openbsc/src/signal.c
index 41352fb..bf5671e 100644
--- a/openbsc/src/signal.c
+++ b/openbsc/src/signal.c
@@ -39,9 +39,6 @@
 {
 	struct signal_handler *sig_data;
 
-	if (!tall_sigh_ctx)
-		tall_sigh_ctx = talloc_named_const(NULL, 1, "signal_handler");
-
 	sig_data = talloc(tall_sigh_ctx, struct signal_handler);
 	if (!sig_data)
 		return -ENOMEM;
@@ -84,3 +81,8 @@
 		(*handler->cbfn)(subsys, signal, handler->data, signal_data);
 	}
 }
+
+static __attribute__((constructor)) void on_dso_load_signal(void)
+{
+	tall_sigh_ctx = talloc_named_const(NULL, 1, "signal_handler");
+}
diff --git a/openbsc/src/subchan_demux.c b/openbsc/src/subchan_demux.c
index ccd4fad..368b985 100644
--- a/openbsc/src/subchan_demux.c
+++ b/openbsc/src/subchan_demux.c
@@ -312,9 +312,6 @@
 {
 	int i;
 
-	if (!tall_tqe_ctx)
-		tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						  "subch_txq_entry");
 	memset(mx, 0, sizeof(*mx));
 	for (i = 0; i < NR_SUBCH; i++) {
 		struct mux_subch *sch = &mx->subch[i];
@@ -323,3 +320,9 @@
 
 	return 0;
 }
+
+static __attribute__((constructor)) void on_dso_load_ss_demux(void)
+{
+	tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 1,
+					  "subch_txq_entry");
+}
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
new file mode 100644
index 0000000..f4cef28
--- /dev/null
+++ b/openbsc/src/transaction.c
@@ -0,0 +1,144 @@
+/* GSM 04.07 Transaction handling */
+
+/* (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 General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <openbsc/transaction.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/mncc.h>
+#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/gsm_04_08.h>
+#include <openbsc/mncc.h>
+#include <openbsc/paging.h>
+
+static void *tall_trans_ctx;
+
+struct gsm_trans *trans_find_by_id(struct gsm_lchan *lchan, u_int8_t trans_id)
+{
+	struct gsm_trans *trans;
+	struct gsm_network *net = lchan->ts->trx->bts->network;
+
+	llist_for_each_entry(trans, &net->trans_list, entry) {
+		if (trans->lchan == lchan && trans->transaction_id == trans_id)
+			return trans;
+	}
+	return NULL;
+}
+
+struct gsm_trans *trans_find_by_callref(struct gsm_network *net,
+					u_int32_t callref)
+{
+	struct gsm_trans *trans;
+
+	llist_for_each_entry(trans, &net->trans_list, entry) {
+		if (trans->callref == callref)
+			return trans;
+	}
+	return NULL;
+}
+
+struct gsm_trans *trans_alloc(struct gsm_subscriber *subscr,
+			      u_int8_t protocol, u_int8_t trans_id,
+			      u_int32_t callref)
+{
+	struct gsm_trans *trans;
+
+	DEBUGP(DCC, "subscr=%p, subscr->net=%p\n", subscr, subscr->net);
+
+	trans = talloc_zero(tall_trans_ctx, struct gsm_trans);
+	if (!trans)
+		return NULL;
+
+	trans->subscr = subscr;
+	subscr_get(trans->subscr);
+
+	trans->protocol = protocol;
+	trans->transaction_id = trans_id;
+	trans->callref = callref;
+
+	llist_add_tail(&trans->entry, &subscr->net->trans_list);
+
+	return trans;
+}
+
+void trans_free(struct gsm_trans *trans)
+{
+	struct gsm_bts *bts;
+
+	switch (trans->protocol) {
+	case GSM48_PDISC_CC:
+		_gsm48_cc_trans_free(trans);
+		break;
+	}
+
+	if (trans->lchan)
+		put_lchan(trans->lchan);
+
+	if (!trans->lchan && trans->subscr && trans->subscr->net) {
+		/* Stop paging on all bts' */
+		bts = NULL;
+		do {
+			bts = gsm_bts_by_lac(trans->subscr->net,
+					     trans->subscr->lac, bts);
+			if (!bts)
+				break;
+			/* Stop paging */
+			paging_request_stop(bts, trans->subscr, NULL);
+		} while (1);
+	}
+
+	if (trans->subscr)
+		subscr_put(trans->subscr);
+
+	llist_del(&trans->entry);
+
+	talloc_free(trans);
+}
+
+/* allocate an unused transaction ID for the given subscriber
+ * in the given protocol using the ti_flag specified */
+int trans_assign_trans_id(struct gsm_subscriber *subscr,
+			  u_int8_t protocol, u_int8_t ti_flag)
+{
+	struct gsm_network *net = subscr->net;
+	struct gsm_trans *trans;
+	unsigned int used_tid_bitmask = 0;
+	int i;
+
+	if (ti_flag)
+		ti_flag = 0x8;
+
+	/* generate bitmask of already-used TIDs for this (subscr,proto) */
+	llist_for_each_entry(trans, &net->trans_list, entry) {
+		if (trans->subscr != subscr ||
+		    trans->protocol != protocol ||
+		    trans->transaction_id == 0xff)
+			continue;
+		used_tid_bitmask |= (1 << trans->transaction_id);
+	}
+
+	for (i = 0; i <= 7; i++) {
+		if ((used_tid_bitmask & (1 << (i | ti_flag))) == 0)
+			return i | ti_flag;
+	}
+
+	return -1;
+}
diff --git a/openbsc/src/trau_mux.c b/openbsc/src/trau_mux.c
index 04febbd..9ff7001 100644
--- a/openbsc/src/trau_mux.c
+++ b/openbsc/src/trau_mux.c
@@ -55,10 +55,6 @@
 {
 	struct map_entry *me;
 
-	if (!tall_map_ctx)
-		tall_map_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						  "trau_map_entry");
-
 	me = talloc(tall_map_ctx, struct map_entry);
 	if (!me)
 		return -ENOMEM;
@@ -201,10 +197,6 @@
 	struct gsm_e1_subslot *src_ss;
 	struct upqueue_entry *ue;
 
-	if (!tall_upq_ctx)
-		tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 1,
-						  "trau_upq_entry");
-
 	ue = talloc(tall_upq_ctx, struct upqueue_entry);
 	if (!ue)
 		return -ENOMEM;
@@ -243,3 +235,12 @@
 	return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out,
 				   TRAU_FRAME_BITS);
 }
+
+static __attribute__((constructor)) void on_dso_load_trau_mux(void)
+{
+	tall_map_ctx = talloc_named_const(tall_bsc_ctx, 1,
+					  "trau_map_entry");
+
+	tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 1,
+					  "trau_upq_entry");
+}
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 4626890..11b2ff6 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -589,7 +589,7 @@
 	const char *imsi = argv[0];
 	struct gsm_subscriber *subscr;
 
-	subscr = subscr_get_by_imsi(imsi);
+	subscr = subscr_get_by_imsi(gsmnet, imsi);
 	if (!subscr) {
 		vty_out(vty, "%% No subscriber for IMSI %s%s",
 			imsi, VTY_NEWLINE);
@@ -855,7 +855,7 @@
 
 	if (argc >= 1) {
 		imsi = argv[0];
-		subscr = subscr_get_by_imsi(imsi);
+		subscr = subscr_get_by_imsi(gsmnet, imsi);
 		if (!subscr) {
 			vty_out(vty, "%% unknown subscriber%s",
 				VTY_NEWLINE);