Merge branch 'master' into config_file

Conflicts:
	openbsc/src/vty_interface.c
diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am
index adef573..c1f3f13 100644
--- a/openbsc/include/openbsc/Makefile.am
+++ b/openbsc/include/openbsc/Makefile.am
@@ -2,4 +2,5 @@
 		 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 rtp_proxy.h
+		 gsm_utils.h ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
+		 bsc_rll.h
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 02bc521..a8734d1 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -476,6 +476,7 @@
 int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val);
 
 int rsl_data_request(struct msgb *msg, u_int8_t link_id);
+int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id);
 int rsl_relase_request(struct gsm_lchan *lchan, u_int8_t link_id);
 
 /* ip.access specfic RSL extensions */
diff --git a/openbsc/include/openbsc/bsc_rll.h b/openbsc/include/openbsc/bsc_rll.h
new file mode 100644
index 0000000..b2898d1
--- /dev/null
+++ b/openbsc/include/openbsc/bsc_rll.h
@@ -0,0 +1,19 @@
+#ifndef _BSC_RLL_H
+#define _BSC_RLL_H
+
+#include <openbsc/gsm_data.h>
+
+enum bsc_rllr_ind {
+	BSC_RLLR_IND_EST_CONF,
+	BSC_RLLR_IND_REL_IND,
+	BSC_RLLR_IND_ERR_IND,
+	BSC_RLLR_IND_TIMEOUT,
+};
+
+int rll_establish(struct gsm_lchan *lchan, u_int8_t link_id,
+		  void (*cb)(struct gsm_lchan *, u_int8_t, void *,
+			     enum bsc_rllr_ind),
+		  void *data);
+void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type);
+
+#endif /* _BSC_RLL_H */
diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h
index d6d367c..38855d1 100644
--- a/openbsc/include/openbsc/chan_alloc.h
+++ b/openbsc/include/openbsc/chan_alloc.h
@@ -37,6 +37,9 @@
 /* Find an allocated channel */
 struct gsm_lchan *lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr);
 
+/* Find an allocated channel for a specified subscriber */
+struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr);
+
 /* Allocate a logical channel (SDCCH, TCH, ...) */
 struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type);
 
diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h
index c9b7265..eef42f6 100644
--- a/openbsc/include/openbsc/db.h
+++ b/openbsc/include/openbsc/db.h
@@ -43,5 +43,6 @@
 /* SMS store-and-forward */
 int db_sms_store(struct gsm_sms *sms);
 struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id);
+struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr);
 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 c93540b..0d83cfb 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -112,6 +112,15 @@
 	u_int8_t t3;
 } __attribute__ ((packed));
 
+/* Section 10.5.2.4 Cell Selection Parameters */
+struct gsm48_cell_sel_par {
+	u_int8_t ms_txpwr_max_ccch:5,	/* GSM 05.08 MS-TXPWR-MAX-CCCH */
+		 cell_resel_hyst:3;	/* GSM 05.08 CELL-RESELECT-HYSTERESIS */
+	u_int8_t rxlev_acc_min:6,	/* GSM 05.08 RXLEV-ACCESS-MIN */
+		 neci:1,
+		 acs:1;
+} __attribute__ ((packed));
+
 /* Section 10.5.2.11 Control Channel Description , Figure 10.5.33 */
 struct gsm48_control_channel_descr {
 	u_int8_t ccch_conf :3,
@@ -157,7 +166,7 @@
 	struct gsm48_loc_area_id lai;
 	struct gsm48_control_channel_descr control_channel_desc;
 	u_int8_t cell_options;
-	u_int8_t cell_selection[2];
+	struct gsm48_cell_sel_par cell_sel_par;
 	struct gsm48_rach_control rach_control;
 	u_int8_t s3_reset_octets[4];
 } __attribute__ ((packed));
@@ -166,7 +175,7 @@
 struct gsm48_system_information_type_4 {
 	struct gsm48_system_information_type_header header;
 	struct gsm48_loc_area_id lai;
-	u_int8_t cell_selection[2];
+	struct gsm48_cell_sel_par cell_sel_par;
 	struct gsm48_rach_control rach_control;
 	/*	optional CBCH conditional CBCH... followed by
 		mandantory SI 4 Reset Octets
diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h
index c59df41..09740e0 100644
--- a/openbsc/include/openbsc/gsm_04_11.h
+++ b/openbsc/include/openbsc/gsm_04_11.h
@@ -50,8 +50,8 @@
 #define GSM411_MT_RP_ACK_MO	0x02
 #define GSM411_MT_RP_ACK_MT	0x03
 #define GSM411_MT_RP_ERROR_MO	0x04
-#define GSM411_MT_RP_ERROR_MT	0x04
-#define GSM411_MT_RP_SMMA_MO	0x05
+#define GSM411_MT_RP_ERROR_MT	0x05
+#define GSM411_MT_RP_SMMA_MO	0x06
 
 enum gsm411_rp_ie {
 	GSM411_IE_RP_USER_DATA		= 0x41,	/* 8.2.5.3 */
diff --git a/openbsc/include/openbsc/gsm_utils.h b/openbsc/include/openbsc/gsm_utils.h
index c468371..7cd0578 100644
--- a/openbsc/include/openbsc/gsm_utils.h
+++ b/openbsc/include/openbsc/gsm_utils.h
@@ -30,4 +30,6 @@
 int gsm_7bit_decode(char *decoded, const u_int8_t *user_data, u_int8_t length);
 int gsm_7bit_encode(u_int8_t *result, const char *data);
 
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm);
+int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl);
 #endif
diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h
index dea6344..1af8496 100644
--- a/openbsc/include/openbsc/signal.h
+++ b/openbsc/include/openbsc/signal.h
@@ -51,6 +51,7 @@
 	S_SMS_SUBMITTED,	/* A SMS has been successfully submitted to us */
 	S_SMS_DELIVERED,	/* A SMS has been successfully delivered to a MS */
 	S_SMS_SMMA,		/* A MS tells us it has more space available */
+	S_SMS_MEM_EXCEEDED,	/* A MS tells us it has no more space available */
 };
 
 /* SS_ABISIP signals */
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index ed63c8e..82415af 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -10,7 +10,7 @@
 		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 \
-		transaction.c rtp_proxy.c
+		transaction.c rtp_proxy.c bsc_rll.c
 
 libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
 
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 726ef23..8f6037f 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -30,8 +30,10 @@
 
 #include <openbsc/gsm_data.h>
 #include <openbsc/gsm_04_08.h>
+#include <openbsc/gsm_utils.h>
 #include <openbsc/abis_rsl.h>
 #include <openbsc/chan_alloc.h>
+#include <openbsc/bsc_rll.h>
 #include <openbsc/debug.h>
 #include <openbsc/tlv.h>
 #include <openbsc/paging.h>
@@ -430,78 +432,6 @@
 	return abis_rsl_sendmsg(msg);
 }
 
-/* determine power control level for given dBm value, as indicated
- * by the tables in chapter 4.1.1 of GSM TS 05.05 */
-static int ms_pwr_ctl_lvl(struct gsm_bts *bts, unsigned int dbm)
-{
-	switch (bts->band) {
-	case GSM_BAND_400:
-	case GSM_BAND_900:
-	case GSM_BAND_850:
-		if (dbm >= 39)
-			return 0;
-		else if (dbm < 5)
-			return 19;
-		else
-			return 2 + ((39 - dbm) / 2);
-		break;
-	case GSM_BAND_1800:
-		if (dbm >= 36)
-			return 29;
-		else if (dbm >= 34)	
-			return 30;
-		else if (dbm >= 32)
-			return 31;
-		else
-			return (30 - dbm) / 2;
-		break;
-	case GSM_BAND_1900:
-		if (dbm >= 33)
-			return 30;
-		else if (dbm >= 32)
-			return 31;
-		else
-			return (30 - dbm) / 2;
-		break;
-	}
-	return -EINVAL;
-}
-
-static int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
-{
-	lvl &= 0x1f;
-
-	switch (band) {
-	case GSM_BAND_400:
-	case GSM_BAND_900:
-	case GSM_BAND_850:
-		if (lvl < 2)
-			return 39;
-		else if (lvl < 20)
-			return 39 - ((lvl - 2) * 2) ;
-		else
-			return 5;
-		break;
-	case GSM_BAND_1800:
-		if (lvl < 16)
-			return 30 - (lvl * 2);
-		else if (lvl < 29)
-			return 0;
-		else
-			return 36 - ((lvl - 29) * 2);
-		break;
-	case GSM_BAND_1900:
-		if (lvl < 16)
-			return 30 - (lvl * 2);
-		else if (lvl < 30)
-			return -EINVAL;
-		else
-			return 33 - (lvl - 30);
-		break;
-	}
-	return -EINVAL;
-}
-
 int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm)
 {
 	struct abis_rsl_dchan_hdr *dh;
@@ -509,7 +439,7 @@
 	u_int8_t chan_nr = lchan2chan_nr(lchan);
 	int ctl_lvl;
 
-	ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts, dbm);
+	ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm);
 	if (ctl_lvl < 0)
 		return ctl_lvl;
 
@@ -840,6 +770,24 @@
 	return abis_rsl_sendmsg(msg);
 }
 
+/* Send "ESTABLISH REQUEST" message with given L3 Info payload */
+/* Chapter 8.3.1 */
+int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id)
+{
+	struct msgb *msg = rsl_msgb_alloc();
+	struct abis_rsl_rll_hdr *rh;
+
+	rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
+	init_llm_hdr(rh, RSL_MT_EST_REQ);
+	//rh->c.msg_discr |= ABIS_RSL_MDISC_TRANSP;
+	rh->chan_nr = lchan2chan_nr(lchan);
+	rh->link_id = link_id;
+
+	msg->trx = lchan->ts->trx;
+
+	return abis_rsl_sendmsg(msg);
+}
+
 /* Chapter 8.3.7 Request the release of multiframe mode of RLL connection.
    This is what higher layers should call.  The BTS then responds with
    RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE,
@@ -889,6 +837,7 @@
 		print_rsl_cause(TLVP_VAL(&tp, RSL_IE_CAUSE),
 				TLVP_LEN(&tp, RSL_IE_CAUSE));
 
+	lchan_free(msg->lchan);
 	return 0;
 }
 
@@ -1111,7 +1060,7 @@
 	arfcn = lchan->ts->trx->arfcn;
 	subch = lchan->nr;
 	
-	lchan->ms_power = ms_pwr_ctl_lvl(bts, 20 /* dBm == 100mW */);
+	lchan->ms_power = ms_pwr_ctl_lvl(bts->band, 20 /* dBm == 100mW */);
 	lchan->bs_power = 0x0f; /* 30dB reduction */
 	lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
 	rsl_chan_activate_lchan(lchan, 0x00, rqd_ta);
@@ -1212,6 +1161,8 @@
 	u_int8_t *rlm_cause = rllh->data;
 
 	DEBUGPC(DRLL, "ERROR INDICATION cause=0x%02x\n", rlm_cause[1]);
+
+	rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
 		
 	if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED)
 		return rsl_chan_release(msg->lchan);
@@ -1254,9 +1205,16 @@
 			return gsm0408_rcvmsg(msg);
 		}
 		break;
+	case RSL_MT_EST_CONF:
+		DEBUGPC(DRLL, "ESTABLISH CONFIRM\n");
+		rll_indication(msg->lchan, rllh->link_id,
+				  BSC_RLLR_IND_EST_CONF);
+		break;
 	case RSL_MT_REL_IND:
 		/* BTS informs us of having received  DISC from MS */
 		DEBUGPC(DRLL, "RELEASE INDICATION\n");
+		rll_indication(msg->lchan, rllh->link_id,
+				  BSC_RLLR_IND_REL_IND);
 		/* we can now releae the channel on the BTS/Abis side */
 		rsl_chan_release(msg->lchan);
 		break;
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index 0759f26..b9d928d 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -37,6 +37,7 @@
 #include <openbsc/db.h>
 #include <openbsc/timer.h>
 #include <openbsc/gsm_data.h>
+#include <openbsc/gsm_utils.h>
 #include <openbsc/gsm_04_08.h>
 #include <openbsc/select.h>
 #include <openbsc/abis_rsl.h>
@@ -964,6 +965,10 @@
 	/* patch TSC */
 	si4[15] &= ~0xe0;
 	si4[15] |= (bts->tsc & 7) << 5;
+
+	/* patch MS max power for CCH */
+	type_4->cell_sel_par.ms_txpwr_max_ccch =
+			ms_pwr_ctl_lvl(bts->band, 20 /* dBm == 100mW */);
 }
 
 
diff --git a/openbsc/src/bsc_rll.c b/openbsc/src/bsc_rll.c
new file mode 100644
index 0000000..780a84e
--- /dev/null
+++ b/openbsc/src/bsc_rll.c
@@ -0,0 +1,118 @@
+/* GSM BSC Radio Link Layer API
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (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 <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/timer.h>
+#include <openbsc/linuxlist.h>
+#include <openbsc/bsc_rll.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/chan_alloc.h>
+#include <openbsc/abis_rsl.h>
+
+struct bsc_rll_req {
+	struct llist_head list;
+	struct timer_list timer;
+
+	struct gsm_lchan *lchan;
+	u_int8_t link_id;
+
+	void (*cb)(struct gsm_lchan *lchan, u_int8_t link_id,
+		   void *data, enum bsc_rllr_ind);
+	void *data;
+};
+
+/* we only compare C1, C2 and SAPI */
+#define LINKID_MASK	0xC7
+
+static LLIST_HEAD(bsc_rll_reqs);
+
+static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type)
+{
+	llist_del(&rllr->list);
+	put_lchan(rllr->lchan);
+	rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type);
+	talloc_free(rllr);
+}
+
+static void timer_cb(void *_rllr)
+{
+	struct bsc_rll_req *rllr = _rllr;
+
+	complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT);
+}
+
+/* establish a RLL connection with given SAPI / priority */
+int rll_establish(struct gsm_lchan *lchan, u_int8_t sapi,
+		  void (*cb)(struct gsm_lchan *, u_int8_t, void *,
+			     enum bsc_rllr_ind),
+		  void *data)
+{
+	struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req);
+	u_int8_t link_id;
+	if (!rllr)
+		return -ENOMEM;
+
+	link_id = sapi;
+
+	/* If we are a TCH and not in signalling mode, we need to
+	 * indicate that the new RLL connection is to be made on the SACCH */
+	if ((lchan->type == GSM_LCHAN_TCH_F ||
+	     lchan->type == GSM_LCHAN_TCH_H) &&
+	    lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
+		link_id |= 0x40;
+
+	use_lchan(lchan);
+	rllr->lchan = lchan;
+	rllr->link_id = link_id;
+	rllr->cb = cb;
+	rllr->data = data;
+
+	llist_add(&rllr->list, &bsc_rll_reqs);
+
+	rllr->timer.cb = &timer_cb;
+	rllr->timer.data = rllr;
+
+	bsc_schedule_timer(&rllr->timer, 10, 0);
+
+	/* send the RSL RLL ESTablish REQuest */
+	return rsl_establish_request(rllr->lchan, rllr->link_id);
+}
+
+/* Called from RSL code in case we have received an indication regarding
+ * any RLL link */
+void rll_indication(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t type)
+{
+	struct bsc_rll_req *rllr, *rllr2;
+
+	llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) {
+		if (rllr->lchan == lchan &&
+		    (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) {
+			bsc_del_timer(&rllr->timer);
+			complete_rllr(rllr, type);
+			return;
+		}
+	}
+}
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index a66f70e..860355a 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -269,3 +269,18 @@
 
 	return NULL;
 }
+
+struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr)
+{
+	struct gsm_bts *bts;
+	struct gsm_network *net = subscr->net;
+	struct gsm_lchan *lchan;
+
+	llist_for_each_entry(bts, &net->bts_list, list) {
+		lchan = lchan_find(bts, subscr);
+		if (lchan)
+			return lchan;
+	}
+
+	return 0;
+}
diff --git a/openbsc/src/db.c b/openbsc/src/db.c
index 7bb3c31..51f94b6 100644
--- a/openbsc/src/db.c
+++ b/openbsc/src/db.c
@@ -44,7 +44,7 @@
 	"INSERT OR IGNORE INTO Meta "
 		"(key, value) "
 		"VALUES "
-		"('revision', '1')",
+		"('revision', '2')",
 	"CREATE TABLE IF NOT EXISTS Subscriber ("
 		"id INTEGER PRIMARY KEY AUTOINCREMENT, "
 		"created TIMESTAMP NOT NULL, "
@@ -87,6 +87,7 @@
 		"status_rep_req INTEGER NOT NULL, "
 		"protocol_id INTEGER NOT NULL, "
 		"data_coding_scheme INTEGER NOT NULL, "
+		"ud_hdr_ind INTEGER NOT NULL, "
 		"dest_addr TEXT, "
 		"user_data BLOB, "	/* TP-UD */
 		/* additional data, interpreted from SMS */
@@ -108,6 +109,30 @@
 	printf("DBI: %s\n", msg);
 }
 
+static int check_db_revision(void)
+{
+	dbi_result result;
+	const char *rev;
+
+	result = dbi_conn_query(conn,
+				"SELECT value FROM Meta WHERE key='revision'");
+	if (!result)
+		return -EINVAL;
+
+	if (!dbi_result_next_row(result)) {
+		dbi_result_free(result);
+		return -EINVAL;
+	}
+	rev = dbi_result_get_string(result, "value");
+	if (!rev || atoi(rev) != 2) {
+		dbi_result_free(result);
+		return -EINVAL;
+	}
+
+	dbi_result_free(result);
+	return 0;
+}
+
 int db_init(const char *name) {
 	dbi_initialize(NULL);
 	conn = dbi_conn_new("sqlite3");
@@ -132,16 +157,25 @@
 	dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname));
 	dbi_conn_set_option(conn, "dbname", basename(db_basename));
 
-	if (dbi_conn_connect(conn) < 0) {
-		free(db_dirname);
-		free(db_basename);
-		db_dirname = db_basename = NULL;
-		return 1;
+	if (dbi_conn_connect(conn) < 0)
+		goto out_err;
+
+	if (check_db_revision() < 0) {
+		fprintf(stderr, "Database schema revision invalid, "
+			"please update your database schema\n");
+		goto out_err;
 	}
 
 	return 0;
+
+out_err:
+	free(db_dirname);
+	free(db_basename);
+	db_dirname = db_basename = NULL;
+	return -1;
 }
 
+
 int db_prepare() {
 	dbi_result result;
 	int i;
@@ -478,13 +512,15 @@
 		"INSERT INTO SMS "
 		"(created, sender_id, receiver_id, valid_until, "
 		 "reply_path_req, status_rep_req, protocol_id, "
-		 "data_coding_scheme, dest_addr, user_data, text) VALUES "
+		 "data_coding_scheme, ud_hdr_ind, dest_addr, "
+		 "user_data, text) VALUES "
 		"(datetime('now'), %llu, %llu, %u, "
-		 "%u, %u, %u, %u, %s, %s, %s)",
+		 "%u, %u, %u, %u, %u, %s, %s, %s)",
 		sms->sender->id,
 		sms->receiver ? sms->receiver->id : 0, validity_timestamp,
 		sms->reply_path_req, sms->status_rep_req, sms->protocol_id,
-		sms->data_coding_scheme, q_daddr, q_udata, q_text);
+		sms->data_coding_scheme, sms->ud_hdr_ind,
+		q_daddr, q_udata, q_text);
 	free(q_text);
 	free(q_daddr);
 	free(q_udata);
@@ -496,32 +532,16 @@
 	return 0;
 }
 
-/* retrieve the next unsent SMS with ID >= min_id */
-struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
+static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result)
 {
-	dbi_result result;
-	long long unsigned int sender_id, receiver_id;
 	struct gsm_sms *sms = sms_alloc();
+	long long unsigned int sender_id, receiver_id;
 	const char *text, *daddr;
 	const unsigned char *user_data;
 
 	if (!sms)
 		return NULL;
 
-	result = dbi_conn_queryf(conn,
-		"SELECT * FROM SMS "
-		"WHERE id >= %llu AND sent is NULL ORDER BY id",
-		min_id);
-	if (!result) {
-		sms_free(sms);
-		return NULL;
-	}
-	if (!dbi_result_next_row(result)) {
-		printf("DB: Failed to find any SMS.\n");
-		dbi_result_free(result);
-		sms_free(sms);
-		return NULL;
-	}
 	sms->id = dbi_result_get_ulonglong(result, "id");
 
 	sender_id = dbi_result_get_ulonglong(result, "sender_id");
@@ -557,8 +577,56 @@
 		strncpy(sms->text, text, sizeof(sms->text));
 		sms->text[sizeof(sms->text)-1] = '\0';
 	}
+	return sms;
+}
+
+/* retrieve the next unsent SMS with ID >= min_id */
+struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
+{
+	dbi_result result;
+	struct gsm_sms *sms;
+
+	result = dbi_conn_queryf(conn,
+		"SELECT * FROM SMS "
+		"WHERE id >= %llu AND sent is NULL ORDER BY id",
+		min_id);
+	if (!result)
+		return NULL;
+
+	if (!dbi_result_next_row(result)) {
+		dbi_result_free(result);
+		return NULL;
+	}
+
+	sms = sms_from_result(net, result);
 
 	dbi_result_free(result);
+
+	return sms;
+}
+
+/* retrieve the next unsent SMS with ID >= min_id */
+struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
+{
+	dbi_result result;
+	struct gsm_sms *sms;
+
+	result = dbi_conn_queryf(conn,
+		"SELECT * FROM SMS "
+		"WHERE receiver_id = %llu AND sent is NULL ORDER BY id",
+		subscr->id);
+	if (!result)
+		return NULL;
+
+	if (!dbi_result_next_row(result)) {
+		dbi_result_free(result);
+		return NULL;
+	}
+
+	sms = sms_from_result(subscr->net, result);
+
+	dbi_result_free(result);
+
 	return sms;
 }
 
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index b753e0d..967e4ce 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -364,11 +364,18 @@
 	u_int32_t tmsi;
 
 	if (authorize_subscriber(lchan->loc_operation, lchan->subscr)) {
+		int rc;
+
 		db_subscriber_alloc_tmsi(lchan->subscr);
-		subscr_update(lchan->subscr, msg->trx->bts, GSM_SUBSCRIBER_UPDATE_ATTACHED);
 		tmsi = strtoul(lchan->subscr->tmsi, NULL, 10);
 		release_loc_updating_req(lchan);
-		return gsm0408_loc_upd_acc(msg->lchan, tmsi);
+		rc = gsm0408_loc_upd_acc(msg->lchan, tmsi);
+		/* call subscr_update after putting the loc_upd_acc
+		 * in the transmit queue, since S_SUBSCR_ATTACHED might
+		 * trigger further action like SMS delivery */
+		subscr_update(lchan->subscr, msg->trx->bts,
+			      GSM_SUBSCRIBER_UPDATE_ATTACHED);
+		return rc;
 	}
 
 	return 0;
@@ -1053,6 +1060,7 @@
 
 	ret = gsm48_sendmsg(msg, NULL);
 
+	/* send MM INFO with network name */
 	ret = gsm48_tx_mm_info(lchan);
 
 	return ret;
@@ -3375,13 +3383,11 @@
 
 int mncc_send(struct gsm_network *net, int msg_type, void *arg)
 {
-	int i, j, k, l, rc = 0;
+	int i, rc = 0;
 	struct gsm_trans *trans = NULL, *transt;
 	struct gsm_subscriber *subscr;
-	struct gsm_lchan *lchan = NULL, *lchant;
+	struct gsm_lchan *lchan = NULL;
 	struct gsm_bts *bts = NULL;
-	struct gsm_bts_trx *trx;
-	struct gsm_bts_trx_ts *ts;
 	struct gsm_mncc *data = arg, rel;
 
 	/* handle special messages */
@@ -3464,23 +3470,7 @@
 			return -ENOMEM;
 		}
 		/* Find lchan */
-		for (i = 0; i < net->num_bts; i++) {
-			bts = gsm_bts_num(net, i);
-			for (j = 0; j < bts->num_trx; j++) {
-				trx = gsm_bts_trx_num(bts, j);
-				for (k = 0; k < TRX_NR_TS; k++) {
-					ts = &trx->ts[k];
-					for (l = 0; l < TS_MAX_LCHAN; l++) {
-						lchant = &ts->lchan[l];
-						if (lchant->subscr == subscr) {
-							lchan = lchant;
-							break;
-						}
-					}
-				}
-			}
-		}
-
+		lchan = lchan_for_subscr(subscr);
 		/* If subscriber has no lchan */
 		if (!lchan) {
 			/* find transaction with this subscriber already paging */
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index 2055510..9a5a08c 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -45,10 +45,13 @@
 #include <openbsc/talloc.h>
 #include <openbsc/transaction.h>
 #include <openbsc/paging.h>
+#include <openbsc/bsc_rll.h>
 
 #define GSM411_ALLOC_SIZE	1024
 #define GSM411_ALLOC_HEADROOM	128
 
+#define UM_SAPI_SMS 3	/* See GSM 04.05/04.06 */
+
 static void *tall_gsms_ctx;
 
 static u_int32_t new_callref = 0x40000001;
@@ -84,7 +87,7 @@
 
 	DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
 
-	return rsl_data_request(msg, 0);
+	return rsl_data_request(msg, UM_SAPI_SMS);
 }
 
 /* Prefix msg with a 04.08/04.11 CP header */
@@ -105,6 +108,7 @@
 	switch (gh->msg_type) {
 	case GSM411_MT_CP_DATA:
 		/* 5.2.3.1.2: enter MO-wait for CP-ack */
+		/* 5.2.3.2.3: enter MT-wait for CP-ACK */
 		trans->sms.cp_state = GSM411_CPS_WAIT_CP_ACK;
 		break;
 	}
@@ -502,6 +506,7 @@
 				rpud_len, rp_ud);
 }
 
+/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */
 static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
 			    struct gsm411_rp_hdr *rph)
 {
@@ -511,13 +516,16 @@
 	 * 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 */
+	if (!trans->sms.is_mt) {
+		DEBUGP(DSMS, "RX RP-ACK on a MO transfer ?\n");
+		return gsm411_send_rp_error(trans, rph->msg_ref,
+					    GSM411_RP_CAUSE_MSG_INCOMP_STATE);
+	}
 
 	if (!sms) {
-		DEBUGP(DSMS, "RX RP-ACK (MT) but no sms in transaction?!?\n");
-		put_lchan(trans->lchan);
-		return -EIO;
+		DEBUGP(DSMS, "RX RP-ACK but no sms in transaction?!?\n");
+		return gsm411_send_rp_error(trans, rph->msg_ref,
+					    GSM411_RP_CAUSE_PROTOCOL_ERR);
 	}
 
 	/* mark this SMS as sent in database */
@@ -528,7 +536,14 @@
 	sms_free(sms);
 	trans->sms.sms = NULL;
 
-	put_lchan(trans->lchan);
+	/* do not free the transaction here, this is done by sending CP-ACK */
+
+	/* check for more messages for this subscriber */
+	sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
+	if (sms)
+		gsm411_send_sms_lchan(msg->lchan, sms);
+	else
+		rsl_release_request(msg->lchan, UM_SAPI_SMS);
 
 	return 0;
 }
@@ -546,19 +561,29 @@
 
 	DEBUGP(DSMS, "RX SMS RP-ERROR Cause=0x%02x\n", cause);
 
-	/* we need to look-up the transaction based on rph->msg_ref to
-	 * identify which particular RP_DATA/SMS-submit failed */
+	if (!trans->sms.is_mt) {
+		DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
+		return gsm411_send_rp_error(trans, rph->msg_ref,
+					    GSM411_RP_CAUSE_MSG_INCOMP_STATE);
+	}
 
 	if (!sms) {
-		DEBUGP(DSMS, "RX RP-ERR (MT) but no sms in transaction?!?\n");
-		put_lchan(trans->lchan);
-		return -EIO;
+		DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
+		return gsm411_send_rp_error(trans, rph->msg_ref,
+					    GSM411_RP_CAUSE_PROTOCOL_ERR);
+	}
+
+	if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) {
+		/* MS has not enough memory to store the message.  We need
+		 * to store this in our database and wati for a SMMA message */
+		/* FIXME */
+		dispatch_signal(SS_SMS, S_SMS_MEM_EXCEEDED, trans->subscr);
 	}
 
 	sms_free(sms);
 	trans->sms.sms = NULL;
 
-	put_lchan(trans->lchan);
+	trans_free(trans);
 
 	return 0;
 }
@@ -566,15 +591,23 @@
 static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans,
 			     struct gsm411_rp_hdr *rph)
 {
+	struct gsm_sms *sms;
 	int rc;
 
+	rc = gsm411_send_rp_ack(trans, rph->msg_ref);
+	trans->sms.rp_state = GSM411_RPS_IDLE;
+
 	/* 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 */
 	dispatch_signal(SS_SMS, S_SMS_SMMA, trans->subscr);
 
-	rc = gsm411_send_rp_ack(trans, rph->msg_ref);
-	trans->sms.rp_state = GSM411_RPS_IDLE;
+	/* check for more messages for this subscriber */
+	sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
+	if (sms)
+		gsm411_send_sms_lchan(msg->lchan, sms);
+	else
+		rsl_release_request(msg->lchan, UM_SAPI_SMS);
 
 	return rc;
 }
@@ -620,8 +653,15 @@
 static int gsm411_tx_cp_ack(struct gsm_trans *trans)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
+	int rc;
 
-	return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK);
+	rc = gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK);
+
+	if (trans->sms.is_mt) {
+		/* If this is a MT SMS DELIVER, we can clear transaction here */
+		trans->sms.cp_state = GSM411_CPS_IDLE;
+		trans_free(trans);
+	}
 }
 
 static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
@@ -672,11 +712,11 @@
 	switch(msg_type) {
 	case GSM411_MT_CP_DATA:
 		DEBUGP(DSMS, "RX SMS CP-DATA\n");
-		if (!trans->sms.is_mt) {
-			/* 5.2.3.1.3: MO state exists when SMC has received
- 			 * CP-DATA, including sending of the assoc. CP-ACK */
-			trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
-		}
+		/* 5.2.3.1.3: MO state exists when SMC has received
+		 * CP-DATA, including sending of the assoc. CP-ACK */
+		/* 5.2.3.2.4: MT state exists when SMC has received
+		 * CP-DATA, including sending of the assoc. CP-ACK */
+		trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
 
 		rc = gsm411_rx_cp_data(msg, gh, trans);
 		/* Send CP-ACK or CP-ERORR in response */
@@ -688,10 +728,11 @@
 	case GSM411_MT_CP_ACK:
 		/* previous CP-DATA in this transaction was confirmed */
 		DEBUGP(DSMS, "RX SMS CP-ACK\n");
+		/* 5.2.3.1.3: MO state exists when SMC has received CP-ACK */
+		/* 5.2.3.2.4: MT state exists when SMC has received CP-ACK */
+		trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
+
 		if (!trans->sms.is_mt) {
-			/* 5.2.3.1.3: MO state exists when SMC has received
- 			 * CP-ACK */
-			trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED;
 			/* FIXME: we have sont one CP-DATA, which was now
 			 * acknowledged.  Check if we want to transfer more,
 			 * i.e. multi-part message */
@@ -707,6 +748,7 @@
 	default:
 		DEBUGP(DSMS, "RX Unimplemented CP msg_type: 0x%02x\n", msg_type);
 		rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST);
+		trans->sms.cp_state = GSM411_CPS_IDLE;
 		trans_free(trans);
 		break;
 	}
@@ -724,16 +766,20 @@
 };
 #endif
 
-/* Take a SMS in gsm_sms structure and send it through lchan */
+/* Take a SMS in gsm_sms structure and send it through an already
+ * existing lchan. We also assume that the caller ensured this lchan already
+ * has a SAPI3 RLL connection! */
 int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
 {
 	struct msgb *msg = gsm411_msgb_alloc();
 	struct gsm_trans *trans;
 	u_int8_t *data, *rp_ud_len;
 	u_int8_t msg_ref = 42;
-	u_int8_t transaction_id = 1;	/* FIXME: random */
+	u_int8_t transaction_id;
 	int rc;
 
+	transaction_id = 4; /* FIXME: we always use 4 for now */
+
 	msg->lchan = lchan;
 
 	DEBUGP(DSMS, "send_sms_lchan()\n");
@@ -793,7 +839,30 @@
 	/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
 }
 
-/* paging callback */
+/* RLL SAPI3 establish callback. Now we have a RLL connection and
+ * can deliver the actual message */
+static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id,
+			void *_sms, enum bsc_rllr_ind type)
+{
+	struct gsm_sms *sms = _sms;
+
+	DEBUGP(DSMS, "rll_ind_cb(lchan=%p, link_id=%u, sms=%p, type=%u\n",
+		lchan, link_id, sms, type);
+
+	switch (type) {
+	case BSC_RLLR_IND_EST_CONF:
+		gsm411_send_sms_lchan(lchan, sms);
+		break;
+	case BSC_RLLR_IND_REL_IND:
+	case BSC_RLLR_IND_ERR_IND:
+	case BSC_RLLR_IND_TIMEOUT:
+		sms_free(sms);
+		break;
+	}
+}
+
+/* paging callback. Here we get called if paging a subscriber has
+ * succeeded or failed. */
 static int paging_cb_send_sms(unsigned int hooknum, unsigned int event,
 			      struct msgb *msg, void *_lchan, void *_sms)
 {
@@ -815,7 +884,8 @@
 			rc = -EIO;
 			break;
 		}
-		rc = gsm411_send_sms_lchan(lchan, sms);
+		/* Establish a SAPI3 RLL connection for SMS */
+		rc = rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms);
 		break;
 	case GSM_PAGING_EXPIRED:
 		sms_free(sms);
@@ -829,13 +899,20 @@
 	return rc;
 }
 
+/* high-level function to send a SMS to a given subscriber. The function
+ * will take care of paging the subscriber, establishing the RLL SAPI3
+ * connection, etc. */
 int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
 			   struct gsm_sms *sms)
 {
+	struct gsm_lchan *lchan;
+
 	/* check if we already have an open lchan to the subscriber.
 	 * if yes, send the SMS this way */
-	//if (subscr->lchan)
-		//return gsm411_send_sms_lchan(subscr->lchan, sms);
+	lchan = lchan_for_subscr(subscr);
+	if (lchan)
+		return rll_establish(lchan, UM_SAPI_SMS,
+				     rll_ind_cb, sms);
 
 	/* if not, we have to start paging */
 	paging_request(subscr->net, subscr, RSL_CHANNEED_SDCCH,
@@ -844,7 +921,36 @@
 	return 0;
 }
 
+static int subscr_sig_cb(unsigned int subsys, unsigned int signal,
+			 void *handler_data, void *signal_data)
+{
+	struct gsm_subscriber *subscr;
+	struct gsm_lchan *lchan;
+	struct gsm_sms *sms;
+
+	switch (signal) {
+	case S_SUBSCR_ATTACHED:
+		/* A subscriber has attached. Check if there are
+		 * any pending SMS for him to be delivered */
+		subscr = signal_data;
+		lchan = lchan_for_subscr(subscr);
+		if (!lchan)
+			break;
+		sms = db_sms_get_unsent_for_subscr(subscr);
+		if (!sms)
+			break;
+		/* Establish a SAPI3 RLL connection for SMS */
+		rll_establish(lchan, UM_SAPI_SMS, rll_ind_cb, sms);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
 static __attribute__((constructor)) void on_dso_load_sms(void)
 {
 	tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 1, "sms");
+
+	register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL);
 }
diff --git a/openbsc/src/gsm_utils.c b/openbsc/src/gsm_utils.c
index 0c25b28..185918e 100644
--- a/openbsc/src/gsm_utils.c
+++ b/openbsc/src/gsm_utils.c
@@ -21,9 +21,11 @@
  *
  */
 
+#include <openbsc/gsm_data.h>
 #include <openbsc/gsm_utils.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
 
 /* GSM 03.38 6.2.1 Charachter packing */
 int gsm_7bit_decode(char *text, const u_int8_t *user_data, u_int8_t length)
@@ -71,3 +73,77 @@
 
 	return out_length;
 }
+
+/* determine power control level for given dBm value, as indicated
+ * by the tables in chapter 4.1.1 of GSM TS 05.05 */
+int ms_pwr_ctl_lvl(enum gsm_band band, unsigned int dbm)
+{
+	switch (band) {
+	case GSM_BAND_400:
+	case GSM_BAND_900:
+	case GSM_BAND_850:
+		if (dbm >= 39)
+			return 0;
+		else if (dbm < 5)
+			return 19;
+		else
+			return 2 + ((39 - dbm) / 2);
+		break;
+	case GSM_BAND_1800:
+		if (dbm >= 36)
+			return 29;
+		else if (dbm >= 34)	
+			return 30;
+		else if (dbm >= 32)
+			return 31;
+		else
+			return (30 - dbm) / 2;
+		break;
+	case GSM_BAND_1900:
+		if (dbm >= 33)
+			return 30;
+		else if (dbm >= 32)
+			return 31;
+		else
+			return (30 - dbm) / 2;
+		break;
+	}
+	return -EINVAL;
+}
+
+int ms_pwr_dbm(enum gsm_band band, u_int8_t lvl)
+{
+	lvl &= 0x1f;
+
+	switch (band) {
+	case GSM_BAND_400:
+	case GSM_BAND_900:
+	case GSM_BAND_850:
+		if (lvl < 2)
+			return 39;
+		else if (lvl < 20)
+			return 39 - ((lvl - 2) * 2) ;
+		else
+			return 5;
+		break;
+	case GSM_BAND_1800:
+		if (lvl < 16)
+			return 30 - (lvl * 2);
+		else if (lvl < 29)
+			return 0;
+		else
+			return 36 - ((lvl - 29) * 2);
+		break;
+	case GSM_BAND_1900:
+		if (lvl < 16)
+			return 30 - (lvl * 2);
+		else if (lvl < 30)
+			return -EINVAL;
+		else
+			return 33 - (lvl - 30);
+		break;
+	}
+	return -EINVAL;
+}
+
+
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index b4aa9f4..05b0070 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -23,6 +23,7 @@
 #include <sys/types.h>
 
 #include <vty/command.h>
+#include <vty/buffer.h>
 #include <vty/vty.h>
 
 #include <arpa/inet.h>
@@ -32,6 +33,7 @@
 #include <openbsc/gsm_subscriber.h>
 #include <openbsc/e1_input.h>
 #include <openbsc/abis_nm.h>
+#include <openbsc/gsm_utils.h>
 #include <openbsc/db.h>
 #include <openbsc/talloc.h>
 
@@ -1139,16 +1141,74 @@
 	return CMD_SUCCESS;
 }
 
+static struct buffer *argv_to_buffer(int argc, const char *argv[], int base)
+{
+	struct buffer *b = buffer_new(1024);
+	int i;
+
+	if (!b)
+		return NULL;
+
+	for (i = base; i < argc; i++) {
+		buffer_putstr(b, argv[i]);
+		buffer_putc(b, ' ');
+	}
+	buffer_putc(b, '\0');
+
+	return b;
+}
+
+static int _send_sms_buffer(struct gsm_subscriber *receiver,
+			     struct buffer *b)
+{
+	struct gsm_sms *sms = sms_alloc();
+
+	if (!sms)
+		return CMD_WARNING;
+
+	if (!receiver->lac) {
+		/* subscriber currently not attached, store in database? */
+		subscr_put(sms->receiver);
+		return CMD_WARNING;
+	}
+
+	sms->receiver = receiver;
+	strncpy(sms->text, buffer_getstr(b), sizeof(sms->text)-1);
+
+	/* FIXME: don't use ID 1 static */
+	sms->sender = subscr_get_by_id(gsmnet, 1);
+	sms->reply_path_req = 0;
+	sms->status_rep_req = 0;
+	sms->ud_hdr_ind = 0;
+	sms->protocol_id = 0; /* implicit */
+	sms->data_coding_scheme = 0; /* default 7bit */
+	strncpy(sms->dest_addr, receiver->extension, sizeof(sms->dest_addr)-1);
+	/* Generate user_data */
+	sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text);
+
+	gsm411_send_sms_subscr(sms->receiver, sms);
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(sms_send_ext,
       sms_send_ext_cmd,
       "sms send extension EXTEN .LINE",
       "Send a message to a subscriber identified by EXTEN")
 {
-	struct gsm_sms *sms;
+	struct gsm_subscriber *receiver;
+	struct buffer *b;
+	int rc;
 
-	//gsm411_send_sms_subscr(sms->receiver, sms);
+	receiver = subscr_get_by_extension(gsmnet, argv[0]);
+	if (!receiver)
+		return CMD_WARNING;
 
-	return CMD_SUCCESS;
+	b = argv_to_buffer(argc, argv, 1);
+	rc = _send_sms_buffer(receiver, b);
+	buffer_free(b);
+
+	return rc;
 }
 
 DEFUN(sms_send_imsi,
@@ -1156,11 +1216,19 @@
       "sms send imsi IMSI .LINE",
       "Send a message to a subscriber identified by IMSI")
 {
-	struct gsm_sms *sms;
+	struct gsm_subscriber *receiver;
+	struct buffer *b;
+	int rc;
 
-	//gsm411_send_sms_subscr(sms->receiver, sms);
+	receiver = subscr_get_by_imsi(gsmnet, argv[0]);
+	if (!receiver)
+		return CMD_WARNING;
 
-	return CMD_SUCCESS;
+	b = argv_to_buffer(argc, argv, 1);
+	rc = _send_sms_buffer(receiver, b);
+	buffer_free(b);
+
+	return rc;
 }
 
 
@@ -1234,10 +1302,8 @@
 	install_element(VIEW_NODE, &show_subscr_cmd);
 
 	install_element(VIEW_NODE, &sms_send_pend_cmd);
-#if 0
 	install_element(VIEW_NODE, &sms_send_ext_cmd);
 	install_element(VIEW_NODE, &sms_send_imsi_cmd);
-#endif
 
 	install_element(CONFIG_NODE, &cfg_net_cmd);
 	install_node(&net_node, config_write_net);