first 'working' SMS implementation

we now have the full path from the MS into the database (SUBMIT), as well as
back from the database to the MS (DELIVER).  The database gets correctly
updated once a SMS has been successfully delivered.

What's still missing is the periodic scan over all undelivered messages,
trying to deliver them to the respective MS.  So far, you have to manually
trigger this on the telnet interface with 'sms send pending 1'
This commit is contained in:
Harald Welte 2009-08-08 16:03:15 +02:00
parent 1d014a5dfc
commit 76042188e0
10 changed files with 373 additions and 209 deletions

View File

@ -109,29 +109,6 @@ enum sms_alphabet {
DCS_8BIT_DATA,
};
/* SMS submit PDU */
struct sms_submit {
u_int8_t *smsc;
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; /* 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;
char decoded[256];
};
/* GSM 03.40 / Chapter 9.2.3.1: TP-Message-Type-Indicator */
#define GSM340_SMS_DELIVER_SC2MS 0x00
#define GSM340_SMS_DELIVER_REP_MS2SC 0x00

View File

@ -361,13 +361,17 @@ struct gsm_sms {
struct gsm_subscriber *receiver;
unsigned long validity_minutes;
unsigned char reply_path_req;
unsigned char status_rep_req;
unsigned char protocol_id;
unsigned char data_coding_scheme;
u_int8_t reply_path_req;
u_int8_t status_rep_req;
u_int8_t ud_hdr_ind;
u_int8_t protocol_id;
u_int8_t data_coding_scheme;
u_int8_t msg_ref;
char dest_addr[20+1]; /* DA LV is 12 bytes max, i.e. 10 bytes
* BCD == 20 bytes string */
u_int8_t user_data_len;
u_int8_t user_data[SMS_TEXT_SIZE];
unsigned int header_len;
unsigned char header[SMS_HDR_SIZE];
char text[SMS_TEXT_SIZE];
};

View File

@ -67,6 +67,8 @@ 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);
struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net,
unsigned long long id);
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,

View File

@ -46,6 +46,13 @@ enum signal_paging {
S_PAGING_COMPLETED,
};
/* SS_SMS signals */
enum signal_sms {
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 */
};
/* SS_ABISIP signals */
enum signal_abisip {
S_ABISIP_BIND_ACK,

View File

@ -44,6 +44,7 @@ struct gsm_trans {
enum gsm411_rp_state rp_state;
struct timer_list timer;
struct gsm_sms *sms;
} sms;
};
};

View File

@ -22,6 +22,7 @@
#include <openbsc/gsm_data.h>
#include <openbsc/db.h>
#include <openbsc/talloc.h>
#include <libgen.h>
#include <stdio.h>
@ -74,18 +75,23 @@ static char *create_stmts[] = {
"UNIQUE (subscriber_id, equipment_id) "
")",
"CREATE TABLE IF NOT EXISTS SMS ("
/* metadata, not part of sms */
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"created TIMESTAMP NOT NULL, "
"sent TIMESTAMP, "
"sender_id INTEGER NOT NULL, "
"receiver_id INTEGER NOT NULL, "
/* data directly copied/derived from SMS */
"valid_until TIMESTAMP, "
"reply_path_req NUMERIC NOT NULL, "
"status_rep_req NUMERIC NOT NULL, "
"protocol_id NUMERIC NOT NULL, "
"data_coding_scheme NUMERIC NOT NULL, "
"sender_id NUMERIC NOT NULL, "
"receiver_id NUMERIC NOT NULL, "
"header BLOB, "
"text TEXT NOT NULL "
"reply_path_req INTEGER NOT NULL, "
"status_rep_req INTEGER NOT NULL, "
"protocol_id INTEGER NOT NULL, "
"data_coding_scheme INTEGER NOT NULL, "
"dest_addr TEXT, "
"user_data BLOB, " /* TP-UD */
/* additional data, interpreted from SMS */
"header BLOB, " /* UD Header */
"text TEXT " /* decoded UD after UDH */
")",
"CREATE TABLE IF NOT EXISTS VLR ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
@ -457,26 +463,31 @@ int db_subscriber_assoc_imei(struct gsm_subscriber* subscriber, char imei[GSM_IM
int db_sms_store(struct gsm_sms *sms)
{
dbi_result result;
char *q_text;
unsigned char *q_header;
char *q_text, *q_daddr;
unsigned char *q_udata;
char *validity_timestamp = "2222-2-2";
/* FIXME: generate validity timestamp based on validity_minutes */
dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text);
dbi_conn_quote_binary_copy(conn, sms->header, sms->header_len,
&q_header);
dbi_conn_quote_string_copy(conn, (char *)sms->dest_addr, &q_daddr);
dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len,
&q_udata);
/* FIXME: correct validity period */
result = dbi_conn_queryf(conn,
"INSERT INTO SMS "
"(created,sender_id,receiver_id,header,text,"
"valid_until,reply_path_req,status_rep_req,"
"protocol_id,data_coding_scheme) VALUES "
"(datetime('now'),%llu,%llu,%s,%s,%s,%u,%u,%u,%u)",
"(created, sender_id, receiver_id, valid_until, "
"reply_path_req, status_rep_req, protocol_id, "
"data_coding_scheme, dest_addr, user_data, text) VALUES "
"(datetime('now'), %llu, %llu, %u, "
"%u, %u, %u, %u, %s, %s, %s)",
sms->sender->id,
sms->receiver ? sms->receiver->id : 0,
q_header, q_text, '2222-2-2', sms->reply_path_req,
sms->status_rep_req, sms->protocol_id,
sms->data_coding_scheme);
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);
free(q_text);
free(q_header);
free(q_daddr);
free(q_udata);
if (!result)
return -EIO;
@ -490,47 +501,62 @@ 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));
const char *text;
const unsigned char *header;
char buf[32];
struct gsm_sms *sms = sms_alloc();
const char *text, *daddr;
const unsigned char *user_data;
if (!sms) {
free(sms);
if (!sms)
return NULL;
}
result = dbi_conn_queryf(conn,
"SELECT * FROM SMS "
"WHERE id >= %llu ORDER BY id", min_id);
"WHERE id >= %llu AND sent is NULL ORDER BY id",
min_id);
if (!result) {
free(sms);
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");
sprintf(buf, "%llu", sender_id);
sms->sender = db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf);
sms->sender = subscr_get_by_id(net, sender_id);
receiver_id = dbi_result_get_ulonglong(result, "receiver_id");
sprintf(buf, "%llu", receiver_id);
sms->receiver = db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf);
sms->receiver = subscr_get_by_id(net, receiver_id);
/* FIXME: validity */
sms->reply_path_req = dbi_result_get_uchar(result, "reply_path_req");
sms->status_rep_req = dbi_result_get_uchar(result, "status_rep_req");
sms->protocol_id = dbi_result_get_uchar(result, "protocol_id");
sms->data_coding_scheme = dbi_result_get_uchar(result,
/* FIXME: those should all be get_uchar, but sqlite3 is braindead */
sms->reply_path_req = dbi_result_get_uint(result, "reply_path_req");
sms->status_rep_req = dbi_result_get_uint(result, "status_rep_req");
sms->ud_hdr_ind = dbi_result_get_uint(result, "ud_hdr_ind");
sms->protocol_id = dbi_result_get_uint(result, "protocol_id");
sms->data_coding_scheme = dbi_result_get_uint(result,
"data_coding_scheme");
/* sms->msg_ref is temporary and not stored in DB */
sms->header_len = dbi_result_get_field_length(result, "header");
header = dbi_result_get_binary(result, "header");
memcpy(sms->header, header, sms->header_len);
daddr = dbi_result_get_string(result, "dest_addr");
if (daddr) {
strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr));
sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0';
}
sms->user_data_len = dbi_result_get_field_length(result, "user_data");
user_data = dbi_result_get_binary(result, "user_data");
if (sms->user_data_len > sizeof(sms->user_data))
sms->user_data_len = sizeof(sms->user_data);
memcpy(sms->user_data, user_data, sms->user_data_len);
text = dbi_result_get_string(result, "text");
if (text)
if (text) {
strncpy(sms->text, text, sizeof(sms->text));
sms->text[sizeof(sms->text)-1] = '\0';
}
dbi_result_free(result);
return sms;

View File

@ -1610,7 +1610,7 @@ static int gsm48_rr_rx_pag_resp(struct msgb *msg)
return -EINVAL;
}
DEBUGP(DRR, "<- Channel was requested by %s\n",
subscr->name ? subscr->name : subscr->imsi);
subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi);
subscr->equipment.classmark2_len = *classmark2_lv;
memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv);
@ -1783,6 +1783,7 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
/* Send actual release request to MS */
gsm48_sendmsg(msg, NULL);
/* FIXME: Start Timer T3109 */
/* Deactivate the SACCH on the BTS side */
return rsl_deact_sacch(lchan);

View File

@ -44,15 +44,31 @@
#include <openbsc/db.h>
#include <openbsc/talloc.h>
#include <openbsc/transaction.h>
#include <openbsc/paging.h>
#define GSM411_ALLOC_SIZE 1024
#define GSM411_ALLOC_HEADROOM 128
static void *tall_sms_ctx;
static void *tall_gsms_ctx;
static u_int32_t new_callref = 0x40000001;
struct gsm_sms *sms_alloc(void)
{
return talloc_zero(tall_gsms_ctx, struct gsm_sms);
}
void sms_free(struct gsm_sms *sms)
{
/* drop references to subscriber structure */
if (sms->sender)
subscr_put(sms->sender);
if (sms->receiver)
subscr_put(sms->receiver);
talloc_free(sms);
}
struct msgb *gsm411_msgb_alloc(void)
{
return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
@ -66,6 +82,8 @@ static int gsm411_sendmsg(struct msgb *msg)
msg->l3h = msg->data;
DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
return rsl_data_request(msg, 0);
}
@ -109,21 +127,15 @@ static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA);
}
#if 0
static u_int8_t gsm0411_tpdu_from_sms(u_int8_t *tpdu, struct sms_deliver *sms)
{
}
#endif
static unsigned long gsm340_validity_period(struct sms_submit *sms)
static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp)
{
u_int8_t vp;
unsigned long minutes;
switch (sms->vpf) {
switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
/* Chapter 9.2.3.12.1 */
vp = *(sms->vp);
vp = *(sms_vp);
if (vp <= 143)
minutes = vp + 1 * 5;
else if (vp <= 167)
@ -182,28 +194,39 @@ enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs)
return alpha;
}
static int gsm340_rx_sms_submit(struct msgb *msg, struct sms_submit *sms,
struct gsm_sms *gsms)
static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
{
if (db_sms_store(gsms) != 0) {
DEBUGP(DSMS, "Failed to store SMS in Database\n");
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
}
/* dispatch a signal to tell higher level about it */
dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
return 0;
}
static int gsm340_gen_oa(u_int8_t *oa, struct gsm_subscriber *subscr)
/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
static int gsm340_gen_oa(u_int8_t *oa, unsigned int oa_len,
struct gsm_subscriber *subscr)
{
int len = 0;
int len_in_bytes;
oa[1] = 0xb9; /* networks-specific number, private numbering plan */
len_in_bytes = encode_bcd_number(oa, oa_len, 1, subscr->extension);
/* GSM 03.40 tells us the length is in 'useful semi-octets' */
oa[0] = strlen(subscr->extension) & 0xff;
return len_in_bytes;
}
static u_int8_t bcdify(u_int8_t value)
{
u_int8_t ret;
ret = value % 10;
ret |= (value / 10) << 4;
ret = value / 10;
ret |= (value % 10) << 4;
return ret;
}
@ -212,7 +235,6 @@ static u_int8_t bcdify(u_int8_t value)
static void gsm340_gen_scts(u_int8_t *scts, time_t time)
{
struct tm *tm = localtime(&time);
u_int8_t digit;
*scts++ = bcdify(tm->tm_year % 100);
*scts++ = bcdify(tm->tm_mon);
@ -223,31 +245,37 @@ static void gsm340_gen_scts(u_int8_t *scts, time_t time)
*scts++ = 0; /* FIXME: timezone */
}
static struct msgb *gsm340_gen_tpdu(struct gsm_sms *sms)
/* generate a msgb containing a TPDU derived from struct gsm_sms,
* returns total size of TPDU */
static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
{
struct msgb *msg = gsm411_msgb_alloc();
u_int8_t *smsp;
u_int8_t oa[12]; /* max len per 03.40 */
u_int8_t oa_len = 0;
unsigned int old_msg_len = msg->len;
/* generate first octet with masked bits */
smsp = msgb_put(msg, 1);
/* TP-MTI (message type indicator) */
*smsp = GSM340_SMS_DELIVER_SC2MS;
if (0 /* FIXME: MMS */)
/* TP-MMS (more messages to send) */
if (0 /* FIXME */)
*smsp |= 0x04;
/* two bits empty */
/* TP-SRI(deliver)/SRR(submit) */
if (sms->status_rep_req)
*smsp |= 0x20;
#if 0
if (sms->header_len)
/* TP-UDHI (indicating TP-UD contains a header) */
if (sms->ud_hdr_ind)
*smsp |= 0x40;
#if 0
/* TP-RP (indicating that a reply path exists) */
if (sms->
*smsp |= 0x80;
#endif
/* generate originator address */
oa_len = gsm340_gen_oa(oa, sizeof(oa), sms->sender);
smsp = msgb_put(msg, oa_len);
oa_len = gsm340_gen_oa(&oa, sms->sender);
memcpy(smsp, oa, oa_len);
/* generate TP-PID */
@ -261,17 +289,16 @@ static struct msgb *gsm340_gen_tpdu(struct gsm_sms *sms)
/* generate TP-SCTS */
smsp = msgb_put(msg, 7);
gsm340_gen_scts(smsp, time(NULL));
#if 0
/* generate TP-UDL */
smsp = msgb_put(msg, 1);
*smsp = ud_len;
*smsp = sms->user_data_len;
/* generate TP-UD */
smsp = msgb_put(msg, ud_len);
memcpy(smsp, FIXME, ud_len);
#endif
smsp = msgb_put(msg, sms->user_data_len);
memcpy(smsp, sms->user_data, sms->user_data_len);
return msg;
return msg->len - old_msg_len;
}
/* process an incoming TPDU (called from RP-DATA)
@ -280,34 +307,27 @@ 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;
u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp;
u_int8_t *sms_vp;
u_int8_t da_len_bytes;
u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
int rc = 0;
sms = talloc(tall_sms_ctx, struct sms_submit);
if (!sms)
gsms = sms_alloc();
if (!gsms)
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
memset(sms, 0, sizeof(*sms));
gsms = talloc(tall_gsms_ctx, struct gsm_sms);
if (!gsms) {
talloc_free(sms);
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
}
memset(gsms, 0, sizeof(*gsms));
/* invert those fields where 0 means active/present */
sms->mti = *smsp & 0x03;
sms->mms = !!(*smsp & 0x04);
sms->vpf = (*smsp & 0x18) >> 3;
sms->srr = (*smsp & 0x20);
sms->udhi= (*smsp & 0x40);
sms->rp = (*smsp & 0x80);
sms_mti = *smsp & 0x03;
sms_mms = !!(*smsp & 0x04);
sms_vpf = (*smsp & 0x18) >> 3;
gsms->status_rep_req = (*smsp & 0x20);
gsms->ud_hdr_ind = (*smsp & 0x40);
sms_rp = (*smsp & 0x80);
smsp++;
sms->msg_ref = *smsp++;
gsms->msg_ref = *smsp++;
/* length in bytes of the destination address */
da_len_bytes = 2 + *smsp/2 + *smsp%2;
@ -321,91 +341,76 @@ static int gsm340_rx_tpdu(struct msgb *msg)
/* mangle first byte to reflect length in bytes, not digits */
address_lv[0] = da_len_bytes - 1;
/* convert to real number */
decode_bcd_number(sms->dest_addr, sizeof(sms->dest_addr), address_lv, 1);
decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_addr), address_lv, 1);
smsp += da_len_bytes;
sms->pid = *smsp++;
gsms->protocol_id = *smsp++;
gsms->data_coding_scheme = *smsp++;
sms->dcs = *smsp++;
sms->alphabet = gsm338_get_sms_alphabet(sms->dcs);
sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme);
switch (sms->vpf) {
switch (sms_vpf) {
case GSM340_TP_VPF_RELATIVE:
sms->vp = smsp++;
sms_vp = smsp++;
break;
case GSM340_TP_VPF_ABSOLUTE:
case GSM340_TP_VPF_ENHANCED:
sms->vp = smsp;
sms_vp = smsp;
smsp += 7;
break;
default:
DEBUGP(DSMS, "SMS Validity period not implemented: 0x%02x\n",
sms->vpf);
sms_vpf);
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
}
sms->ud_len = *smsp++;
if (sms->ud_len)
sms->user_data = smsp;
else
sms->user_data = NULL;
gsms->user_data_len = *smsp++;
if (gsms->user_data_len) {
memcpy(gsms->user_data, smsp, gsms->user_data_len);
if (sms->ud_len) {
switch (sms->alphabet) {
switch (sms_alphabet) {
case DCS_7BIT_DEFAULT:
gsm_7bit_decode(sms->decoded, smsp, sms->ud_len);
gsm_7bit_decode(gsms->text, smsp, gsms->user_data_len);
break;
case DCS_8BIT_DATA:
case DCS_UCS2:
case DCS_NONE:
memcpy(sms->decoded, sms->user_data, sms->ud_len);
break;
}
}
DEBUGP(DSMS, "SMS:\nMTI: 0x%02x, VPF: 0x%02x, MR: 0x%02x "
"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));
"UserData: \"%s\"\n", sms_mti, sms_vpf, gsms->msg_ref,
gsms->protocol_id, gsms->data_coding_scheme,
gsms->dest_addr, gsms->user_data_len,
sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
hexdump(gsms->user_data, gsms->user_data_len));
dispatch_signal(SS_SMS, 0, sms);
gsms->sender = subscr_get(msg->lchan->subscr);
/* now we've filled the 'sms' structure. Go on filling
* the gsms structure based on information from the sms */
gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
gsms->sender = msg->lchan->subscr;
/* FIXME: sender refcount */
gsms->validity_minutes = gsm340_validity_period(sms);
dispatch_signal(SS_SMS, 0, gsms);
/* determine gsms->receiver based on dialled number */
gsms->receiver = subscr_get_by_extension(bts->network, sms->dest_addr);
gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
if (!gsms->receiver) {
rc = 1; /* cause 1: unknown subscriber */
goto out;
}
if (sms->user_data) {
gsms->header_len = sms->ud_len;
memcpy(gsms->header, sms->user_data, sms->ud_len);
}
if (sms->decoded)
strncpy(gsms->text, sms->decoded, sizeof(gsms->text));
switch (sms->mti) {
switch (sms_mti) {
case GSM340_SMS_SUBMIT_MS2SC:
/* MS is submitting a SMS */
rc = gsm340_rx_sms_submit(msg, sms, gsms);
rc = gsm340_rx_sms_submit(msg, gsms);
break;
case GSM340_SMS_COMMAND_MS2SC:
case GSM340_SMS_DELIVER_REP_MS2SC:
DEBUGP(DSMS, "Unimplemented MTI 0x%02x\n", sms->mti);
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);
DEBUGP(DSMS, "Undefined MTI 0x%02x\n", sms_mti);
rc = GSM411_RP_CAUSE_IE_NOTEXIST;
break;
}
@ -414,8 +419,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED;
out:
talloc_free(gsms);
talloc_free(sms);
sms_free(gsms);
return rc;
}
@ -448,7 +452,6 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
u_int8_t dst_len, u_int8_t *dst,
u_int8_t tpdu_len, u_int8_t *tpdu)
{
struct gsm48_hdr *gh = msgb_l3(msg);
int rc = 0;
if (src_len && src)
@ -499,10 +502,11 @@ static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans,
rpud_len, rp_ud);
}
static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
struct gsm411_rp_hdr *rph)
{
struct gsm_sms *sms = trans->sms.sms;
/* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it
* successfully received a SMS. We can now safely mark it as
* transmitted */
@ -510,11 +514,29 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
/* we need to look-up the transaction based on rph->msg_ref to
* identify which particular RP_DATA/SMS-submit was ACKed */
if (!sms) {
DEBUGP(DSMS, "RX RP-ACK (MT) but no sms in transaction?!?\n");
put_lchan(trans->lchan);
return -EIO;
}
/* mark this SMS as sent in database */
db_sms_mark_sent(sms);
dispatch_signal(SS_SMS, S_SMS_DELIVERED, sms);
sms_free(sms);
trans->sms.sms = NULL;
put_lchan(trans->lchan);
return 0;
}
static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
struct gsm411_rp_hdr *rph)
{
struct gsm_sms *sms = trans->sms.sms;
u_int8_t cause_len = rph->data[0];
u_int8_t cause = rph->data[1];
@ -527,6 +549,17 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
/* we need to look-up the transaction based on rph->msg_ref to
* identify which particular RP_DATA/SMS-submit failed */
if (!sms) {
DEBUGP(DSMS, "RX RP-ERR (MT) but no sms in transaction?!?\n");
put_lchan(trans->lchan);
return -EIO;
}
sms_free(sms);
trans->sms.sms = NULL;
put_lchan(trans->lchan);
return 0;
}
@ -538,6 +571,7 @@ static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans,
/* 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;
@ -595,7 +629,7 @@ static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause)
struct msgb *msg = gsm411_msgb_alloc();
u_int8_t *causep;
cause = msgb_put(msg, 1);
causep = msgb_put(msg, 1);
*causep = cause;
return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ERROR);
@ -680,18 +714,7 @@ int gsm0411_rcv_sms(struct msgb *msg)
return rc;
}
/* Test TPDU - 25c3 welcome */
#if 0
static u_int8_t tpdu_test[] = {
0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x92, 0x90, 0x32,
0x24, 0x40, 0x4D, 0xB2, 0xDA, 0x70, 0xD6, 0x9A, 0x97, 0xE5, 0xF6, 0xF4,
0xB8, 0x0C, 0x0A, 0xBB, 0xDD, 0xEF, 0xBA, 0x7B, 0x5C, 0x6E, 0x97, 0xDD,
0x74, 0x1D, 0x08, 0xCA, 0x2E, 0x87, 0xE7, 0x65, 0x50, 0x98, 0x4E, 0x2F,
0xBB, 0xC9, 0x20, 0x3A, 0xBA, 0x0C, 0x3A, 0x4E, 0x9B, 0x20, 0x7A, 0x98,
0xBD, 0x06, 0x85, 0xE9, 0xA0, 0x58, 0x4C, 0x37, 0x83, 0x81, 0xD2, 0x6E,
0xD0, 0x34, 0x1C, 0x66, 0x83, 0x62, 0x21, 0x90, 0xAE, 0x95, 0x02
};
#else
/* Test TPDU - ALL YOUR */
static u_int8_t tpdu_test[] = {
0x04, 0x04, 0x81, 0x32, 0x24, 0x00, 0x00, 0x80, 0x21, 0x03, 0x41, 0x24,
@ -701,19 +724,37 @@ static u_int8_t tpdu_test[] = {
};
#endif
int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms)
/* Take a SMS in gsm_sms structure and send it through lchan */
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;
u_int8_t *data, *rp_ud_len;
u_int8_t msg_ref = 42;
u_int8_t trans_id = 23;
u_int8_t transaction_id = 1; /* FIXME: random */
int rc;
msg->lchan = lchan;
/* FIXME: allocate trans */
DEBUGP(DSMS, "send_sms_lchan()\n");
/* Hardcode Originating Address for now */
/* FIXME: allocate transaction with message reference */
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
transaction_id, new_callref++);
if (!trans) {
DEBUGP(DSMS, "No memory for trans\n");
/* FIXME: send some error message */
return -ENOMEM;
}
trans->sms.cp_state = GSM411_CPS_IDLE;
trans->sms.rp_state = GSM411_RPS_IDLE;
trans->sms.is_mt = 1;
trans->sms.sms = sms;
trans->lchan = lchan;
use_lchan(lchan);
/* Hardcode SMSC Originating Address for now */
data = (u_int8_t *)msgb_put(msg, 8);
data[0] = 0x07; /* originator length == 7 */
data[1] = 0x91; /* type of number: international, ISDN */
@ -728,45 +769,82 @@ int gsm0411_send_sms(struct gsm_lchan *lchan, struct sms_deliver *sms)
data = (u_int8_t *)msgb_put(msg, 1);
data[0] = 0; /* destination length == 0 */
/* FIXME: Hardcoded for now */
//smslen = gsm0411_tpdu_from_sms(tpdu, sms);
/* obtain a pointer for the rp_ud_len, so we can fill it later */
rp_ud_len = (u_int8_t *)msgb_put(msg, 1);
/* RPDU length */
data = (u_int8_t *)msgb_put(msg, 1);
data[0] = sizeof(tpdu_test);
#if 1
/* generate the 03.40 TPDU */
rc = gsm340_gen_tpdu(msg, sms);
if (rc < 0) {
msgb_free(msg);
return rc;
}
data = (u_int8_t *)msgb_put(msg, sizeof(tpdu_test));
//memcpy(data, tpdu, smslen);
*rp_ud_len = rc;
#else
data = msgb_put(msg, sizeof(tpdu_test));
memcpy(data, tpdu_test, sizeof(tpdu_test));
*rp_ud_len = sizeof(tpdu_test);
#endif
DEBUGP(DSMS, "TX: SMS SUBMIT\n");
DEBUGP(DSMS, "TX: SMS DELIVER\n");
return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
}
#if 0
/* paging callback */
static int paging_cb_send_sms(unsigned int hooknum, unsigned int event,
struct msgb *msg, void *_lchan, void *_sms)
{
struct sms_deliver *smsd;
struct gsm_lchan *lchan = _lchan;
struct gsm_sms *sms = _sms;
int rc;
smsd->mti = GSM340_SMS_DELIVER_SC2MS;
smsd->mms = 0; /* FIXME: determine if there are more */
smsd->rp = FIXME;
smsd->udhi = FIXME;
smsd->sri = 1;
smsd->oa = FIXME;
smsd->pid = FIXME;
smsd->dcs = FIXME;
smsd->scts = FIXME;
smsd->ud_len = FIXME;
smsd->ud = FIXME;
}
#endif
DEBUGP(DSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p,"
"lchan=%p, sms=%p)\n", hooknum, event, msg, lchan, sms);
if (hooknum != GSM_HOOK_RR_PAGING)
return -EINVAL;
switch (event) {
case GSM_PAGING_SUCCEEDED:
/* Paging aborted without lchan ?!? */
if (!lchan) {
sms_free(sms);
rc = -EIO;
break;
}
rc = gsm411_send_sms_lchan(lchan, sms);
break;
case GSM_PAGING_EXPIRED:
sms_free(sms);
rc = -ETIMEDOUT;
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
int gsm411_send_sms_subscr(struct gsm_subscriber *subscr,
struct gsm_sms *sms)
{
/* 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);
/* if not, we have to start paging */
paging_request(subscr->net, subscr, RSL_CHANNEED_SDCCH,
paging_cb_send_sms, sms);
return 0;
}
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");
}

View File

@ -163,6 +163,22 @@ struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext);
}
struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net,
unsigned long long id)
{
struct gsm_subscriber *subscr;
char buf[32];
sprintf(buf, "%llu", id);
llist_for_each_entry(subscr, &active_subscribers, entry) {
if (subscr->id == id)
return subscr_get(subscr);
}
return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf);
}
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
{
/* FIXME: Migrate pending requests from one BSC to another */

View File

@ -954,6 +954,52 @@ DEFUN(show_subscr,
return CMD_SUCCESS;
}
DEFUN(sms_send_pend,
sms_send_pend_cmd,
"sms send pending MIN_ID",
"Send all pending SMS starting from MIN_ID")
{
struct gsm_sms *sms;
sms = db_sms_get_unsent(gsmnet, atoi(argv[0]));
if (!sms)
return CMD_WARNING;
if (!sms->receiver) {
sms_free(sms);
return CMD_WARNING;
}
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;
//gsm411_send_sms_subscr(sms->receiver, sms);
return CMD_SUCCESS;
}
DEFUN(sms_send_imsi,
sms_send_imsi_cmd,
"sms send imsi IMSI .LINE",
"Send a message to a subscriber identified by IMSI")
{
struct gsm_sms *sms;
//gsm411_send_sms_subscr(sms->receiver, sms);
return CMD_SUCCESS;
}
DEFUN(cfg_subscr_name,
cfg_subscr_name_cmd,
"name NAME",
@ -1023,6 +1069,12 @@ int bsc_vty_init(struct gsm_network *net)
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_bts_cmd);
install_node(&bts_node, config_write_bts);
install_default(BTS_NODE);