Merge branch 'master' into sms
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_11.h b/openbsc/include/openbsc/gsm_04_11.h
index 12c607f..4f45357 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -16,6 +16,19 @@
 	GSM411_CP_IE_CAUSE		= 0x02,	/* 8.1.4.2. */
 };
 
+/* Section 8.1.4.2 / Table 8.2 */
+enum gsm411_cp_cause {
+	GSM411_CP_CAUSE_NET_FAIL	= 17,
+	GSM411_CP_CAUSE_CONGESTION	= 22,
+	GSM411_CP_CAUSE_INV_TRANS_ID	= 81,
+	GSM411_CP_CAUSE_SEMANT_INC_MSG	= 95,
+	GSM411_CP_CAUSE_INV_MAND_INF	= 96,
+	GSM411_CP_CAUSE_MSGTYPE_NOTEXIST= 97,
+	GSM411_CP_CAUSE_MSG_INCOMP_STATE= 98,
+	GSM411_CP_CAUSE_IE_NOTEXIST	= 99,
+	GSM411_CP_CAUSE_PROTOCOL_ERR	= 111,
+};
+
 /* Chapter 8.2.2 */
 #define GSM411_MT_RP_DATA_MO	0x00
 #define GSM411_MT_RP_DATA_MT	0x01
@@ -30,6 +43,36 @@
 	GSM411_IE_RP_CAUSE		= 0x42,	/* 8.2.5.4 */
 };
 
+/* Chapter 8.2.5.4 Table 8.4 */
+enum gsm411_rp_cause {
+	/* valid only for MO */
+	GSM411_RP_CAUSE_MO_NUM_UNASSIGNED	= 1,
+	GSM411_RP_CAUSE_MO_OP_DET_BARR		= 8,
+	GSM411_RP_CAUSE_MO_CALL_BARRED		= 10,
+	GSM411_RP_CAUSE_MO_SMS_REJECTED		= 21,
+	GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER	= 27,
+	GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR	= 28,
+	GSM411_RP_CAUSE_MO_FACILITY_REJ		= 29,
+	GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR	= 30,
+	GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER	= 38,
+	GSM411_RP_CAUSE_MO_TEMP_FAIL		= 41,
+	GSM411_RP_CAUSE_MO_CONGESTION		= 42,
+	GSM411_RP_CAUSE_MO_RES_UNAVAIL		= 47,
+	GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR	= 50,
+	GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL	= 69,
+	GSM411_RP_CAUSE_MO_INTERWORKING		= 127,
+	/* valid only for MT */
+	GSM411_RP_CAUSE_MT_MEM_EXCEEDED		= 22,
+	/* valid for both directions */
+	GSM411_RP_CAUSE_INV_TRANS_REF		= 81,
+	GSM411_RP_CAUSE_SEMANT_INC_MSG		= 95,
+	GSM411_RP_CAUSE_INV_MAND_INF		= 96,
+	GSM411_RP_CAUSE_MSGTYPE_NOTEXIST	= 97,
+	GSM411_RP_CAUSE_MSG_INCOMP_STATE	= 98,
+	GSM411_RP_CAUSE_IE_NOTEXIST		= 99,
+	GSM411_RP_CAUSE_PROTOCOL_ERR		= 111,
+};
+
 /* Chapter 8.2.1 */
 struct gsm411_rp_hdr {
 	u_int8_t len;
@@ -49,20 +92,20 @@
 /* SMS submit PDU */
 struct sms_submit {
 	u_int8_t *smsc;
-	u_int8_t mti:2;
-	u_int8_t vpf:2;
-	u_int8_t msg_ref;
-	u_int8_t pid;
-	u_int8_t dcs;
-	u_int8_t *vp;
-	u_int8_t ud_len;
-	u_int8_t *user_data;
+	u_int8_t mti:2;		/* message type indicator */
+	u_int8_t vpf:2;		/* validity period format */
+	u_int8_t msg_ref;	/* message reference */
+	u_int8_t pid;		/* protocol identifier */
+	u_int8_t dcs;		/* data coding scheme */
+	u_int8_t *vp;		/* validity period */
+	u_int8_t ud_len;	/* user data length */
+	u_int8_t *user_data;	/* user data */
 
 	/* interpreted */
-	u_int8_t mms:1;
-	u_int8_t sri:1;
-	u_int8_t udhi:1;
-	u_int8_t rp:1;
+	u_int8_t mms:1;		/* more messages to send */
+	u_int8_t srr:1;		/* status report request */
+	u_int8_t udhi:1;	/* user data headre indication */
+	u_int8_t rp:1;		/* request for reply path */
 	enum sms_alphabet alphabet;
 	char dest_addr[20+1];	/* DA LV is 12 bytes max, i.e. 10 bytes BCD == 20 bytes string */
 	unsigned long validity_mins;
@@ -144,20 +187,20 @@
 
 /* SMS deliver PDU */
 struct sms_deliver {
+	u_int8_t mti:2;		/* message type indicator */
+	u_int8_t mms:1;		/* more messages to send */
+	u_int8_t rp:1;		/* reply path */
+	u_int8_t udhi:1;	/* user data header indicator */
+	u_int8_t sri:1;		/* status report indication */
+	u_int8_t *orig_addr;	/* originating address */
+	u_int8_t pid;		/* protocol identifier */
+	u_int8_t dcs;		/* data coding scheme */
+				/* service centre time stamp */
+	u_int8_t ud_len;	/* user data length */
+	u_int8_t *user_data;	/* user data */
+
+	u_int8_t msg_ref;	/* message reference */
 	u_int8_t *smsc;
-	u_int8_t mti:2;
-	u_int8_t rd:1;
-	u_int8_t vpf:2;
-	u_int8_t srr:1;
-	u_int8_t udhi:1;
-	u_int8_t rp:1;
-	u_int8_t msg_ref;
-	u_int8_t *orig_addr;
-	u_int8_t pid;
-	u_int8_t dcs;
-	u_int8_t vp;
-	u_int8_t ud_len;
-	u_int8_t *user_data;
 };
 
 struct msgb;
@@ -166,7 +209,4 @@
 
 int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms);
 
-struct msgb *gsm411_msgb_alloc(void);
-int gsm0411_sendmsg(struct msgb *msg);
-
 #endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index de3f1f5..087123f 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -74,7 +74,7 @@
  * Use the channel. As side effect the lchannel recycle timer
  * will be started.
  */
-#define LCHAN_RELEASE_TIMEOUT 10, 0
+#define LCHAN_RELEASE_TIMEOUT 20, 0
 #define use_lchan(lchan) \
 	do {	lchan->use_count++; \
 		DEBUGP(DCC, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
@@ -104,29 +104,36 @@
 	/* 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;
+	};
 };
 
 
@@ -381,6 +388,8 @@
 	struct gsm_subscriber *sender;
 	struct gsm_subscriber *receiver;
 
+	unsigned long validity_minutes;
+	unsigned int header_len;
 	unsigned char header[SMS_HDR_SIZE];
 	char text[SMS_TEXT_SIZE];
 };
diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h
index 4a23115..f182230 100644
--- a/openbsc/include/openbsc/gsm_subscriber.h
+++ b/openbsc/include/openbsc/gsm_subscriber.h
@@ -50,6 +50,7 @@
 	GSM_SUBSCRIBER_IMSI,
 	GSM_SUBSCRIBER_TMSI,
 	GSM_SUBSCRIBER_EXTENSION,
+	GSM_SUBSCRIBER_ID,
 };
 
 enum gsm_subscriber_update_reason {
@@ -60,9 +61,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/src/Makefile.am b/openbsc/src/Makefile.am
index c0ac63c..c6e9dae 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
 
 libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
 
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 543f44c..57c5c85 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -79,7 +79,7 @@
 		"sent TIMESTAMP, "
 		"sender_id NUMERIC NOT NULL, "
 		"receiver_id NUMERIC NOT NULL, "
-		"header NUMERIC, "
+		"header BLOB, "
 		"text TEXT NOT NULL "
 		")",
 	"CREATE TABLE IF NOT EXISTS VLR ("
@@ -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;
@@ -230,6 +235,13 @@
 		);
 		free(quoted);
 		break;
+	case GSM_SUBSCRIBER_ID:
+		dbi_conn_quote_string_copy(conn, id, &quoted);
+		result = dbi_conn_queryf(conn,
+			"SELECT * FROM Subscriber "
+			"WHERE id = %s ", quoted);
+		free(quoted);
+		break;
 	default:
 		printf("DB: Unknown query selector for Subscriber.\n");
 		return NULL;
@@ -246,6 +258,7 @@
 	}
 
 	subscr = subscr_alloc();
+	subscr->net = net;
 	subscr->id = dbi_result_get_ulonglong(result, "id");
 	string = dbi_result_get_string(result, "imsi");
 	if (string)
@@ -440,16 +453,20 @@
 {
 	dbi_result result;
 	char *q_text;
+	unsigned char *q_header;
 
 	dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
+	dbi_conn_quote_binary_copy(conn, sms->header, sms->header_len,
+				   &q_header);
 	result = dbi_conn_queryf(conn,
 		"INSERT INTO SMS "
 		"(created,sender_id,receiver_id,header,text) VALUES "
 		"(datetime('now'),%llu,%llu,%s,%s)",
 		sms->sender->id,
 		sms->receiver ? sms->receiver->id : 0,
-		NULL, q_text);
+		q_header, q_text);
 	free(q_text);
+	free(q_header);
 
 	if (!result)
 		return -EIO;
@@ -459,10 +476,13 @@
 }
 
 /* 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;
+	long long unsigned int sender_id, receiver_id;
 	struct gsm_sms *sms = malloc(sizeof(*sms));
+	char *text;
+	char buf[32];
 
 	if (!sms) {
 		free(sms);
@@ -476,8 +496,21 @@
 		free(sms);
 		return NULL;
 	}
+	sms->id = dbi_result_get_ulonglong(result, "id");
 
-	/* FIXME: fill gsm_sms from database */
+	sender_id = dbi_result_get_ulonglong(result, "sender_id");
+	sprintf(buf, "%llu", sender_id);
+	sms->sender = db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); 
+
+	receiver_id = dbi_result_get_ulonglong(result, "receiver_id");
+	sprintf(buf, "%llu", receiver_id);
+	sms->receiver = db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); 
+
+	/* FIXME: fill header */
+
+	text = dbi_result_get_string(result, "text");
+	if (text)
+		strncpy(sms->text, text, sizeof(sms->text));
 
 	dbi_result_free(result);
 	return sms;
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index df4d3c6..44f8672 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -45,6 +45,7 @@
 #include <openbsc/trau_frame.h>
 #include <openbsc/trau_mux.h>
 #include <openbsc/talloc.h>
+#include <openbsc/transaction.h>
 
 #define GSM48_ALLOC_SIZE	1024
 #define GSM48_ALLOC_HEADROOM	128
@@ -54,7 +55,6 @@
 #define GSM_MAX_USERUSER       128
 
 static void *tall_locop_ctx;
-static void *tall_trans_ctx;
 
 static const struct tlv_definition rsl_att_tlvdef = {
 	.def = {
@@ -240,7 +240,7 @@
 		rep->flags |= MEAS_REP_F_BA1;
 	if (data[0] & 0x40)
 		rep->flags |= MEAS_REP_F_DTX;
-	if (data[1] & 0x40)
+	if ((data[1] & 0x40) == 0x00)
 		rep->flags |= MEAS_REP_F_VALID;
 
 	rep->rxlev_full = data[0] & 0x3f;
@@ -292,7 +292,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;
@@ -392,9 +391,12 @@
 	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;
@@ -1129,6 +1131,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 +1143,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 +1200,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 +1235,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 +1244,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);
@@ -1421,6 +1426,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 +1461,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 +1484,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 +1498,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 +1581,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 +1596,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;
 	}
 
@@ -1789,9 +1797,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)
@@ -1831,10 +1839,10 @@
 
 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;
 	}
 }
  
@@ -1883,49 +1891,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);
@@ -1966,7 +1948,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;
@@ -1974,11 +1956,12 @@
 			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;
 		}
 	}
@@ -2027,21 +2010,11 @@
 	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;
@@ -2059,7 +2032,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)
@@ -2077,7 +2050,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)
@@ -2116,7 +2089,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;
@@ -2134,21 +2107,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;
@@ -2159,7 +2132,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;
 	}
@@ -2167,15 +2140,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)
@@ -2190,10 +2163,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)
@@ -2268,7 +2241,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;
 }
@@ -2289,16 +2262,16 @@
 		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) {
+	llist_for_each_entry(transt, &trans->subscr->net->trans_list, entry) {
 		/* Transaction of our lchan? */
 		if (transt->lchan == trans->lchan &&
 		    transt->transaction_id != 0xff)
@@ -2307,11 +2280,11 @@
 	/* Assign free transaction ID */
 	if ((trans_id_mask & 0x007f) == 0x7f) {
 		/* 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++) {
@@ -2398,7 +2371,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)
@@ -2461,7 +2435,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)
@@ -2580,7 +2555,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);
 }
 
 
@@ -2594,7 +2569,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);
 }
 
@@ -2651,7 +2626,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);
 
 }
 
@@ -2695,7 +2670,7 @@
 		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);
 
@@ -2740,19 +2715,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,
+				     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;
 }
@@ -2782,10 +2758,10 @@
 	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);
@@ -2830,23 +2806,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;
 }
@@ -2875,7 +2851,7 @@
 	if (rel->fields & MNCC_F_USERUSER)
 		encode_useruser(msg, 0, &rel->useruser);
 
-	free_trans(trans);
+	trans_free(trans);
 
 	return gsm48_sendmsg(msg);
 }
@@ -2903,7 +2879,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)
@@ -2928,7 +2904,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)
@@ -2968,7 +2944,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)
@@ -3019,7 +2996,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)
@@ -3077,7 +3054,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)
@@ -3099,7 +3076,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)
@@ -3143,7 +3120,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)
@@ -3191,7 +3168,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)
@@ -3243,7 +3220,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)
@@ -3286,7 +3263,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)
@@ -3385,7 +3362,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) {
@@ -3410,9 +3387,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) "
@@ -3437,7 +3415,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 */
@@ -3446,12 +3425,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);
@@ -3487,7 +3460,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;
@@ -3525,7 +3498,7 @@
 		else
 			rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel);
 		trans->callref = 0;
-		free_trans(trans);
+		trans_free(trans);
 		return rc;
 	}
 
@@ -3534,13 +3507,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");
@@ -3613,8 +3586,7 @@
 	u_int8_t msg_type = gh->msg_type & 0xbf;
 	u_int8_t transaction_id = (gh->proto_discr & 0xf0) ^ 0x80; /* 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) {
@@ -3623,50 +3595,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;
-		}
-	}
-	
+	trans = trans_find_by_id(lchan, transaction_id);
+
 	DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x 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, "
 			"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_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");
@@ -3823,3 +3783,4 @@
 
 	return work;
 }
+
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 1b622b1..5279291 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -55,7 +55,7 @@
 				   "GSM 04.11");
 }
 
-int gsm0411_sendmsg(struct msgb *msg)
+static int gsm411_sendmsg(struct msgb *msg)
 {
 	if (msg->lchan)
 		msg->trx = msg->lchan->ts->trx;
@@ -65,6 +65,35 @@
 	return rsl_data_request(msg, 0);
 }
 
+/* Prefix msg with a 04.08/04.11 CP header */
+static int gsm411_cp_sendmsg(struct msgb *msg, u_int8_t msg_type,
+			     u_int8_t trans_id)
+{
+	struct gsm48_hdr *gh;
+
+	gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
+	/* Outgoing needs the highest bit set */
+	gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
+	gh->msg_type = msg_type;
+
+	return gsm411_sendmsg(msg);
+}
+
+/* Prefix msg with a RP-DATA header and send as CP-DATA */
+static int gsm411_rp_sendmsg(struct msgb *msg, u_int8_t rp_msg_type,
+			     u_int8_t rp_msg_ref, u_int8_t cp_trans_id)
+{
+	struct gsm411_rp_hdr *rp;
+
+	/* GSM 04.11 RP-DATA header */
+	rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
+	rp->len = msg->len;
+	rp->msg_type = rp_msg_type;
+	rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
+
+	return gsm411_cp_sendmsg(msg, GSM411_MT_CP_DATA, cp_trans_id);
+}
+
 
 #if 0
 static u_int8_t gsm0411_tpdu_from_sms(u_int8_t *tpdu, struct sms_deliver *sms)
@@ -144,16 +173,16 @@
 {
 	if (db_sms_store(gsms) != 0) {
 		DEBUGP(DSMS, "Failed to store SMS in Database\n");
-		talloc_free(sms);
-		talloc_free(gsms);
-		return -EIO;
+		return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
 	}
 	return 0;
 }
 
-/* process an incoming TPDU (called from RP-DATA) */
+/* process an incoming TPDU (called from RP-DATA) 
+ * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ 
 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;
@@ -167,7 +196,7 @@
 
 	sms = talloc(tall_sms_ctx, struct sms_submit);
 	if (!sms)
-		return -ENOMEM;
+		return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
 	memset(sms, 0, sizeof(*sms));
 
 	if (!tall_gsms_ctx)
@@ -177,7 +206,7 @@
 	gsms = talloc(tall_gsms_ctx, struct gsm_sms);
 	if (!gsms) {
 		talloc_free(sms);
-		return -ENOMEM;
+		return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
 	}
 	memset(gsms, 0, sizeof(*gsms));
 
@@ -185,7 +214,7 @@
 	sms->mti = *smsp & 0x03;
 	sms->mms = !!(*smsp & 0x04);
 	sms->vpf = (*smsp & 0x18) >> 3;
-	sms->sri = !!(*smsp & 0x20);
+	sms->srr = !!(*smsp & 0x20);
 	sms->udhi= !!(*smsp & 0x40);
 	sms->rp  = !!(*smsp & 0x80);
 
@@ -196,7 +225,7 @@
 	da_len_bytes = 2 + *smsp/2 + *smsp%2;
 	if (da_len_bytes > 12) {
 		DEBUGP(DSMS, "Destination Address > 12 bytes ?!?\n");
-		rc = -EIO;
+		rc = GSM411_RP_CAUSE_SEMANT_INC_MSG;
 		goto out;
 	}
 	memset(address_lv, 0, sizeof(address_lv));
@@ -249,21 +278,25 @@
 			"PID: 0x%02x, DCS: 0x%02x, DA: %s, UserDataLength: 0x%02x "
 			"UserData: \"%s\"\n", sms->mti, sms->vpf, sms->msg_ref,
 			sms->pid, sms->dcs, sms->dest_addr, sms->ud_len,
-			sms->alphabet == DCS_7BIT_DEFAULT ? sms->decoded : hexdump(sms->user_data, sms->ud_len));
+			sms->alphabet == DCS_7BIT_DEFAULT ? sms->decoded : 
+					hexdump(sms->user_data, sms->ud_len));
 
 	dispatch_signal(SS_SMS, 0, sms);
 
+	/* now we've filled the 'sms' structure.  Go on filling
+	 * the gsms structure based on information from the sms */
+
 	gsms->sender = msg->lchan->subscr;
 	/* FIXME: sender refcount */
 
-	/* determine gsms->receiver based on dialled number */
-	gsms->receiver = subscr_get_by_extension(sms->dest_addr);
-	if (!gsms->receiver) {
-		rc = 1; /* cause 1: unknown subscriber */
-		goto out;
-	}
+	gsms->validity_minutes = gsm340_validity_period(sms);
 
+	/* determine gsms->receiver based on dialled number */
+	gsms->receiver = subscr_get_by_extension(bts->network, sms->dest_addr);
 	if (sms->user_data)
+		memcpy(gsms->header, sms->user_data, sms->ud_len);
+
+	if (sms->decoded)
 		strncpy(gsms->text, sms->decoded, sizeof(gsms->text));
 
 	switch (sms->mti) {
@@ -274,12 +307,17 @@
 	case GSM340_SMS_COMMAND_MS2SC:
 	case GSM340_SMS_DELIVER_REP_MS2SC:
 		DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms->mti);
+		rc = GSM411_RP_CAUSE_IE_NOTEXIST;
 		break;
 	default:
 		DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms->mti);
+		rc = GSM411_RP_CAUSE_IE_NOTEXIST;
 		break;
 	}
 
+	if (!rc && !gsms->receiver)
+		rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
+
 out:
 	talloc_free(gsms);
 	talloc_free(sms);
@@ -291,48 +329,26 @@
 		u_int8_t msg_ref)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
-	struct gsm48_hdr *gh;
-	struct gsm411_rp_hdr *rp;
 
 	msg->lchan = lchan;
 
-	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-	// Outgoing needs the highest bit set
-	gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
-	gh->msg_type = GSM411_MT_CP_DATA;
-
-	rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
-	rp->len = 2;
-	rp->msg_type = GSM411_MT_RP_ACK_MT;
-	rp->msg_ref = msg_ref;
-
 	DEBUGP(DSMS, "TX: SMS RP ACK\n");
 
-	return gsm0411_sendmsg(msg);
+	return gsm411_rp_sendmsg(msg, GSM411_MT_RP_ACK_MT, msg_ref, trans_id);
 }
 
 static int gsm411_send_rp_error(struct gsm_lchan *lchan, u_int8_t trans_id,
 		u_int8_t msg_ref, u_int8_t cause)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
-	struct gsm48_hdr *gh;
-	struct gsm411_rp_hdr *rp;
 
 	msg->lchan = lchan;
 
-	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-	// Outgoing needs the highest bit set
-	gh->proto_discr = GSM48_PDISC_SMS | trans_id<<4 | 0x80;
-	gh->msg_type = GSM411_MT_CP_DATA;
-
-	rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
-	rp->msg_type = GSM411_MT_RP_ERROR_MT;
-	rp->msg_ref = msg_ref;
 	msgb_tv_put(msg, 1, cause);
 
 	DEBUGP(DSMS, "TX: SMS RP ERROR (cause %02d)\n", cause);
 
-	return gsm0411_sendmsg(msg);
+	return gsm411_rp_sendmsg(msg, GSM411_MT_RP_ERROR_MT, msg_ref, trans_id);
 }
 
 /* Receive a 04.11 TPDU inside RP-DATA / user data */
@@ -350,12 +366,13 @@
 
 	if (!dst_len || !dst || !tpdu_len || !tpdu) {
 		DEBUGP(DSMS, "RP-DATA (MO) without DST or TPDU ?!?\n");
+		gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref,
+				     GSM411_RP_CAUSE_INV_MAND_INF);
 		return -EIO;
 	}
 	msg->smsh = tpdu;
 
 	DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
-	//return gsm411_send_rp_error(msg->lchan, trans_id, rph->msg_ref, rc);
 
 	rc = gsm340_rx_tpdu(msg);
 	if (rc == 0)
@@ -385,11 +402,42 @@
 	if (rpud_len)
 		rp_ud = &rph->data[1+src_len+1+dst_len+1];
 
-	DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", src_len, dst_len, rpud_len);
+	DEBUGP(DSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n",
+		src_len, dst_len, rpud_len);
 	return gsm411_rx_rp_ud(msg, rph, src_len, src, dst_len, dst,
 				rpud_len, rp_ud);
 }
 
+
+static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm411_rp_hdr *rph)
+{
+	/* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it
+	 * successfully received a SMS.  We can now safely mark it as
+	 * transmitted */
+
+	/* we need to look-up the transaction based on rph->msg_ref to
+	 * identify which particular RP_DATA/SMS-submit was ACKed */
+
+}
+
+static int gsm411_rx_rp_error(struct msgb *msg, struct gsm411_rp_hdr *rph)
+{
+	/* Error in response to MT RP_DATA, i.e. the MS did not
+	 * successfully receive the SMS.  We need to investigate
+	 * the cause and take action depending on it */
+
+	/* we need to look-up the transaction based on rph->msg_ref to
+	 * identify which particular RP_DATA/SMS-submit failed */
+
+}
+
+static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm411_rp_hdr *rph)
+{
+	/* MS tells us that it has memory for more SMS, we need
+	 * to check if we have any pending messages for it and then
+	 * transfer those */
+}
+
 static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh)
 {
 	struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data;
@@ -402,16 +450,17 @@
 		rc = gsm411_rx_rp_data(msg, rp_data);
 		break;
 	case GSM411_MT_RP_ACK_MO:
-		/* Acnkowledgement to MT RP_DATA */
+		rc = gsm411_rx_rp_ack(msg, rp_data);
+		break;
 	case GSM411_MT_RP_ERROR_MO:
-		/* Error in response to MT RP_DATA */
+		rc = gsm411_rx_rp_error(msg, rp_data);
+		break;
 	case GSM411_MT_RP_SMMA_MO:
-		/* MS tells us that it has memory for more SMS, we need
-		 * to check if we have any pending messages for it and then
-		 * transfer those */
+		rc = gsm411_rx_rp_smma(msg, rp_data);
 		DEBUGP(DSMS, "Unimplemented RP type 0x%02x\n", msg_type);
 		break;
 	default:
+		/* FIXME: send GSM411_CP_CAUSE_MSGTYPE_NOTEXIST */
 		DEBUGP(DSMS, "Invalid RP type 0x%02x\n", msg_type);
 		break;
 	}
@@ -469,32 +518,26 @@
 int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
-	struct gsm48_hdr *gh;
-	struct gsm411_rp_hdr *rp;
 	u_int8_t *data;
+	u_int8_t msg_ref = 42;
+	u_int8_t trans_id = 23;
 
 	msg->lchan = lchan;
 
-	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
-	gh->proto_discr = GSM48_PDISC_SMS;
-	gh->msg_type = GSM411_MT_CP_DATA;
-
-	rp = (struct gsm411_rp_hdr *)msgb_put(msg, sizeof(*rp));
-	rp->len = sizeof(tpdu_test) + 10;
-	rp->msg_type = GSM411_MT_RP_DATA_MT;
-	rp->msg_ref = 42; /* FIXME: Choose randomly */
-	/* Hardcode OA for now */
+	/* Hardcode Originating Address for now */
 	data = (u_int8_t *)msgb_put(msg, 8);
-	data[0] = 0x07;
-	data[1] = 0x91;
-	data[2] = 0x44;
+	data[0] = 0x07;	/* originator length == 7 */
+	data[1] = 0x91; /* type of number: international, ISDN */
+	data[2] = 0x44; /* 447785016005 */
 	data[3] = 0x77;
 	data[4] = 0x58;
 	data[5] = 0x10;
 	data[6] = 0x06;
 	data[7] = 0x50;
+
+	/* Hardcoded Destination Address */
 	data = (u_int8_t *)msgb_put(msg, 1);
-	data[0] = 0;
+	data[0] = 0;	/* destination length == 0 */
 
 	/* FIXME: Hardcoded for now */
 	//smslen = gsm0411_tpdu_from_sms(tpdu, sms);
@@ -510,5 +553,5 @@
 
 	DEBUGP(DSMS, "TX: SMS SUBMIT\n");
 
-	return gsm0411_sendmsg(msg);
+	return gsm411_rp_sendmsg(msg, GSM411_MT_RP_DATA_MT, msg_ref, trans_id);
 }
diff --git a/openbsc/src/gsm_subscriber.c b/openbsc/src/gsm_subscriber.c
index a323d4e..60dec43 100644
--- a/openbsc/src/gsm_subscriber.c
+++ b/openbsc/src/gsm_subscriber.c
@@ -126,7 +126,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 +137,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 +150,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 +163,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 +171,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;
@@ -175,7 +179,6 @@
 		/* Only detach if we are currently in this area */
 		if (bts->location_area_code == s->lac)
 			s->lac = 0;
-
 		break;
 	default:
 		fprintf(stderr, "subscr_update with unknown reason: %d\n",
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);