mirror of
https://gerrit.osmocom.org/osmo-gbproxy
synced 2024-08-18 08:56:23 +00:00
Merge branch 'holger/merge-msc-bsc-split'
This commit is contained in:
commit
20054e84b4
18 changed files with 2209 additions and 1786 deletions
|
@ -48,4 +48,9 @@ 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);
|
||||
|
||||
/* APDU blob storage */
|
||||
int db_apdu_blob_store(struct gsm_subscriber *subscr,
|
||||
u_int8_t apdu_id_flags, u_int8_t len,
|
||||
u_int8_t *apdu);
|
||||
#endif /* _DB_H */
|
||||
|
|
|
@ -681,6 +681,11 @@ enum gsm48_bcap_rrq {
|
|||
GSM48_BCAP_RRQ_DUAL_FR = 3,
|
||||
};
|
||||
|
||||
|
||||
#define GSM48_TMSI_LEN 5
|
||||
#define GSM48_MID_TMSI_LEN (GSM48_TMSI_LEN + 2)
|
||||
|
||||
|
||||
struct msgb;
|
||||
struct gsm_bts;
|
||||
struct gsm_subscriber;
|
||||
|
@ -705,6 +710,8 @@ int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans);
|
|||
int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi);
|
||||
|
||||
int gsm48_send_rr_release(struct gsm_lchan *lchan);
|
||||
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
|
||||
u_int8_t apdu_len, u_int8_t *apdu);
|
||||
|
||||
int bsc_upqueue(struct gsm_network *net);
|
||||
|
||||
|
@ -716,4 +723,6 @@ int encode_bcd_number(u_int8_t *bcd_lv, u_int8_t max_len,
|
|||
int decode_bcd_number(char *output, int output_len, const u_int8_t *bcd_lv,
|
||||
int h_len);
|
||||
|
||||
extern const char *gsm0408_cc_msg_names[];
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,20 +2,24 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
|||
AM_CFLAGS=-Wall
|
||||
|
||||
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config isdnsync
|
||||
noinst_LIBRARIES = libbsc.a libvty.a
|
||||
noinst_LIBRARIES = libbsc.a libmsc.a libvty.a
|
||||
noinst_HEADERS = vty/cardshell.h
|
||||
|
||||
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c mncc.c \
|
||||
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 \
|
||||
libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
|
||||
msgb.c select.c chan_alloc.c timer.c debug.c \
|
||||
gsm_subscriber_base.c subchan_demux.c bsc_rll.c transaction.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 talloc_ctx.c \
|
||||
transaction.c rtp_proxy.c bsc_rll.c token_auth.c
|
||||
input/misdn.c input/ipaccess.c signal.c gsm_utils.c talloc.c \
|
||||
talloc_ctx.c
|
||||
|
||||
libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
|
||||
mncc.c rtp_proxy.c gsm_04_08.c gsm_04_11.c transaction.c \
|
||||
token_auth.c rrlp.c
|
||||
|
||||
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
|
||||
|
||||
bsc_hack_SOURCES = bsc_hack.c vty_interface.c
|
||||
bsc_hack_LDADD = libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
bsc_hack_SOURCES = bsc_hack.c bsc_init.c vty_interface.c vty_interface_layer3.c
|
||||
bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
|
||||
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \
|
||||
select.c timer.c rs232.c tlv_parser.c signal.c talloc.c
|
||||
|
@ -23,6 +27,6 @@ bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c \
|
|||
ipaccess_find_SOURCES = ipaccess-find.c select.c timer.c
|
||||
|
||||
ipaccess_config_SOURCES = ipaccess-config.c
|
||||
ipaccess_config_LDADD = libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
|
||||
isdnsync_SOURCES = isdnsync.c
|
||||
|
|
File diff suppressed because it is too large
Load diff
1064
openbsc/src/bsc_init.c
Normal file
1064
openbsc/src/bsc_init.c
Normal file
File diff suppressed because it is too large
Load diff
131
openbsc/src/db.c
131
openbsc/src/db.c
|
@ -24,6 +24,7 @@
|
|||
#include <openbsc/gsm_04_11.h>
|
||||
#include <openbsc/db.h>
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
|
@ -88,6 +89,7 @@ static char *create_stmts[] = {
|
|||
"sent TIMESTAMP, "
|
||||
"sender_id INTEGER NOT NULL, "
|
||||
"receiver_id INTEGER NOT NULL, "
|
||||
"deliver_attempts INTEGER NOT NULL DEFAULT 0, "
|
||||
/* data directly copied/derived from SMS */
|
||||
"valid_until TIMESTAMP, "
|
||||
"reply_path_req INTEGER NOT NULL, "
|
||||
|
@ -108,6 +110,13 @@ static char *create_stmts[] = {
|
|||
"subscriber_id NUMERIC UNIQUE NOT NULL, "
|
||||
"last_bts NUMERIC NOT NULL "
|
||||
")",
|
||||
"CREATE TABLE IF NOT EXISTS ApduBlobs ("
|
||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
"created TIMESTAMP NOT NULL, "
|
||||
"apdu_id_flags INTEGER NOT NULL, "
|
||||
"subscriber_id INTEGER NOT NULL, "
|
||||
"apdu BLOB "
|
||||
")",
|
||||
};
|
||||
|
||||
void db_error_func(dbi_conn conn, void* data) {
|
||||
|
@ -252,6 +261,53 @@ struct gsm_subscriber* db_create_subscriber(struct gsm_network *net, char *imsi)
|
|||
return subscr;
|
||||
}
|
||||
|
||||
static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
|
||||
{
|
||||
dbi_result result;
|
||||
char *string;
|
||||
unsigned int cm1;
|
||||
const unsigned char *cm2, *cm3;
|
||||
struct gsm_equipment *equip = &subscr->equipment;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT equipment.* FROM equipment,equipmentwatch "
|
||||
"WHERE equipmentwatch.equipment_id=equipment.id "
|
||||
"AND equipmentwatch.subscriber_id = %llu "
|
||||
"ORDER BY updated DESC", subscr->id);
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
if (!dbi_result_next_row(result)) {
|
||||
dbi_result_free(result);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
equip->id = dbi_result_get_ulonglong(result, "id");
|
||||
|
||||
string = dbi_result_get_string(result, "imei");
|
||||
if (string)
|
||||
strncpy(equip->imei, string, sizeof(equip->imei));
|
||||
|
||||
cm1 = dbi_result_get_uint(result, "classmark1") & 0xff;
|
||||
equip->classmark1 = *((struct gsm48_classmark1 *) &cm1);
|
||||
|
||||
equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
|
||||
cm2 = dbi_result_get_binary(result, "classmark2");
|
||||
if (equip->classmark2_len > sizeof(equip->classmark2))
|
||||
equip->classmark2_len = sizeof(equip->classmark2);
|
||||
memcpy(equip->classmark2, cm2, equip->classmark2_len);
|
||||
|
||||
equip->classmark3_len = dbi_result_get_field_length(result, "classmark3");
|
||||
cm3 = dbi_result_get_binary(result, "classmark3");
|
||||
if (equip->classmark3_len > sizeof(equip->classmark3))
|
||||
equip->classmark3_len = sizeof(equip->classmark3);
|
||||
memcpy(equip->classmark3, cm3, equip->classmark3_len);
|
||||
|
||||
dbi_result_free(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#define BASE_QUERY "SELECT * FROM Subscriber "
|
||||
struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
||||
enum gsm_subscriber_field field,
|
||||
const char *id)
|
||||
|
@ -265,7 +321,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
case GSM_SUBSCRIBER_IMSI:
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
BASE_QUERY
|
||||
"WHERE imsi = %s ",
|
||||
quoted
|
||||
);
|
||||
|
@ -274,7 +330,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
case GSM_SUBSCRIBER_TMSI:
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
BASE_QUERY
|
||||
"WHERE tmsi = %s ",
|
||||
quoted
|
||||
);
|
||||
|
@ -283,7 +339,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
case GSM_SUBSCRIBER_EXTENSION:
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
BASE_QUERY
|
||||
"WHERE extension = %s ",
|
||||
quoted
|
||||
);
|
||||
|
@ -292,7 +348,7 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
case GSM_SUBSCRIBER_ID:
|
||||
dbi_conn_quote_string_copy(conn, id, "ed);
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM Subscriber "
|
||||
BASE_QUERY
|
||||
"WHERE id = %s ", quoted);
|
||||
free(quoted);
|
||||
break;
|
||||
|
@ -336,6 +392,9 @@ struct gsm_subscriber *db_get_subscriber(struct gsm_network *net,
|
|||
subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension,
|
||||
subscr->lac, subscr->authorized);
|
||||
dbi_result_free(result);
|
||||
|
||||
get_equipment_by_subscr(subscr);
|
||||
|
||||
return subscr;
|
||||
}
|
||||
|
||||
|
@ -378,6 +437,16 @@ int db_sync_equipment(struct gsm_equipment *equip)
|
|||
dbi_result result;
|
||||
unsigned char *cm2, *cm3;
|
||||
|
||||
printf("DB: Sync Equipment IMEI=%s, classmark1=%02x",
|
||||
equip->imei, equip->classmark1);
|
||||
if (equip->classmark2_len)
|
||||
printf(", classmark2=%s",
|
||||
hexdump(equip->classmark2, equip->classmark2_len));
|
||||
if (equip->classmark3_len)
|
||||
printf(", classmark3=%s",
|
||||
hexdump(equip->classmark3, equip->classmark3_len));
|
||||
printf("\n");
|
||||
|
||||
dbi_conn_quote_binary_copy(conn, equip->classmark2,
|
||||
equip->classmark2_len, &cm2);
|
||||
dbi_conn_quote_binary_copy(conn, equip->classmark3,
|
||||
|
@ -688,8 +757,10 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
|
|||
struct gsm_sms *sms;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM SMS "
|
||||
"WHERE id >= %llu AND sent is NULL ORDER BY id",
|
||||
"SELECT * FROM SMS,Subscriber "
|
||||
"WHERE sms.id >= %llu AND sms.sent is NULL "
|
||||
"AND subscriber.lac > 0 "
|
||||
"ORDER BY id",
|
||||
min_id);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
@ -713,8 +784,10 @@ struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr)
|
|||
struct gsm_sms *sms;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM SMS "
|
||||
"WHERE receiver_id = %llu AND sent is NULL ORDER BY id",
|
||||
"SELECT * FROM SMS,Subscriber "
|
||||
"WHERE sms.receiver_id = %llu AND sms.sent is NULL "
|
||||
"AND subscriber.lac > 0 "
|
||||
"ORDER BY id",
|
||||
subscr->id);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
@ -748,3 +821,45 @@ int db_sms_mark_sent(struct gsm_sms *sms)
|
|||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* increase the number of attempted deliveries */
|
||||
int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
|
||||
{
|
||||
dbi_result result;
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE SMS "
|
||||
"SET deliver_attempts = deliver_attempts + 1 "
|
||||
"WHERE id = %llu", sms->id);
|
||||
if (!result) {
|
||||
printf("DB: Failed to inc deliver attempts for SMS %llu.\n", sms->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_apdu_blob_store(struct gsm_subscriber *subscr,
|
||||
u_int8_t apdu_id_flags, u_int8_t len,
|
||||
u_int8_t *apdu)
|
||||
{
|
||||
dbi_result result;
|
||||
char *q_apdu;
|
||||
|
||||
dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu);
|
||||
|
||||
result = dbi_conn_queryf(conn,
|
||||
"INSERT INTO ApduBlobs "
|
||||
"(created,subscriber_id,apdu_id_flags,apdu) VALUES "
|
||||
"(datetime('now'),%llu,%u,%s)",
|
||||
subscr->id, apdu_id_flags, q_apdu);
|
||||
|
||||
free(q_apdu);
|
||||
|
||||
if (!result)
|
||||
return -EIO;
|
||||
|
||||
dbi_result_free(result);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -49,18 +49,13 @@
|
|||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/transaction.h>
|
||||
|
||||
#define GSM48_ALLOC_SIZE 1024
|
||||
#define GSM48_ALLOC_HEADROOM 128
|
||||
|
||||
#define GSM_MAX_FACILITY 128
|
||||
#define GSM_MAX_SSVERSION 128
|
||||
#define GSM_MAX_USERUSER 128
|
||||
|
||||
void *tall_locop_ctx;
|
||||
|
||||
/* should ip.access BTS use direct RTP streams between each other (1),
|
||||
* or should OpenBSC always act as RTP relay/proxy in between (0) ? */
|
||||
int ipacc_rtp_direct = 1;
|
||||
extern int ipacc_rtp_direct;
|
||||
|
||||
static const struct tlv_definition rsl_att_tlvdef = {
|
||||
.def = {
|
||||
|
@ -158,73 +153,6 @@ static const char *cc_state_names[] = {
|
|||
"illegal state 31",
|
||||
};
|
||||
|
||||
static const char *cc_msg_names[] = {
|
||||
"unknown 0x00",
|
||||
"ALERTING",
|
||||
"CALL_PROC",
|
||||
"PROGRESS",
|
||||
"ESTAB",
|
||||
"SETUP",
|
||||
"ESTAB_CONF",
|
||||
"CONNECT",
|
||||
"CALL_CONF",
|
||||
"START_CC",
|
||||
"unknown 0x0a",
|
||||
"RECALL",
|
||||
"unknown 0x0c",
|
||||
"unknown 0x0d",
|
||||
"EMERG_SETUP",
|
||||
"CONNECT_ACK",
|
||||
"USER_INFO",
|
||||
"unknown 0x11",
|
||||
"unknown 0x12",
|
||||
"MODIFY_REJECT",
|
||||
"unknown 0x14",
|
||||
"unknown 0x15",
|
||||
"unknown 0x16",
|
||||
"MODIFY",
|
||||
"HOLD",
|
||||
"HOLD_ACK",
|
||||
"HOLD_REJ",
|
||||
"unknown 0x1b",
|
||||
"RETR",
|
||||
"RETR_ACK",
|
||||
"RETR_REJ",
|
||||
"MODIFY_COMPL",
|
||||
"unknown 0x20",
|
||||
"unknown 0x21",
|
||||
"unknown 0x22",
|
||||
"unknown 0x23",
|
||||
"unknown 0x24",
|
||||
"DISCONNECT",
|
||||
"unknown 0x26",
|
||||
"unknown 0x27",
|
||||
"unknown 0x28",
|
||||
"unknown 0x29",
|
||||
"RELEASE_COMPL",
|
||||
"unknown 0x2b",
|
||||
"unknown 0x2c",
|
||||
"RELEASE",
|
||||
"unknown 0x2e",
|
||||
"unknown 0x2f",
|
||||
"unknown 0x30",
|
||||
"STOP_DTMF",
|
||||
"STOP_DTMF_ACK",
|
||||
"unknown 0x33",
|
||||
"STATUS_ENQ",
|
||||
"START_DTMF",
|
||||
"START_DTMF_ACK",
|
||||
"START_DTMF_REJ",
|
||||
"unknown 0x38",
|
||||
"CONG_CTRL",
|
||||
"FACILITY",
|
||||
"unknown 0x3b",
|
||||
"STATUS",
|
||||
"unknown 0x3c",
|
||||
"NOTIFY",
|
||||
"unknown 0x3f",
|
||||
};
|
||||
|
||||
static char strbuf[64];
|
||||
|
||||
static const char *rr_cause_name(u_int8_t cause)
|
||||
|
@ -413,53 +341,6 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void to_bcd(u_int8_t *bcd, u_int16_t val)
|
||||
{
|
||||
bcd[2] = val % 10;
|
||||
val = val / 10;
|
||||
bcd[1] = val % 10;
|
||||
val = val / 10;
|
||||
bcd[0] = val % 10;
|
||||
val = val / 10;
|
||||
}
|
||||
|
||||
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
|
||||
u_int16_t mnc, u_int16_t lac)
|
||||
{
|
||||
u_int8_t bcd[3];
|
||||
|
||||
to_bcd(bcd, mcc);
|
||||
lai48->digits[0] = bcd[0] | (bcd[1] << 4);
|
||||
lai48->digits[1] = bcd[2];
|
||||
|
||||
to_bcd(bcd, mnc);
|
||||
/* FIXME: do we need three-digit MNC? See Table 10.5.3 */
|
||||
#if 0
|
||||
lai48->digits[1] |= bcd[2] << 4;
|
||||
lai48->digits[2] = bcd[0] | (bcd[1] << 4);
|
||||
#else
|
||||
lai48->digits[1] |= 0xf << 4;
|
||||
lai48->digits[2] = bcd[1] | (bcd[2] << 4);
|
||||
#endif
|
||||
|
||||
lai48->lac = htons(lac);
|
||||
}
|
||||
|
||||
#define TMSI_LEN 5
|
||||
#define MID_TMSI_LEN (TMSI_LEN + 2)
|
||||
|
||||
int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi)
|
||||
{
|
||||
u_int32_t *tptr = (u_int32_t *) &buf[3];
|
||||
|
||||
buf[0] = GSM48_IE_MOBILE_ID;
|
||||
buf[1] = TMSI_LEN;
|
||||
buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
|
||||
*tptr = htonl(tmsi);
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
static const char bcd_num_digits[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||
'8', '9', '*', '#', 'a', 'b', 'c', '\0'
|
||||
|
@ -979,44 +860,6 @@ static int encode_more(struct msgb *msg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct msgb *gsm48_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM,
|
||||
"GSM 04.08");
|
||||
}
|
||||
|
||||
int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans)
|
||||
{
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
|
||||
|
||||
/* if we get passed a transaction reference, do some common
|
||||
* work that the caller no longer has to do */
|
||||
if (trans) {
|
||||
gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
|
||||
msg->lchan = trans->lchan;
|
||||
}
|
||||
|
||||
if (msg->lchan) {
|
||||
msg->trx = msg->lchan->ts->trx;
|
||||
|
||||
if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC)
|
||||
DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) "
|
||||
"Sending '%s' to MS.\n", msg->trx->bts->nr,
|
||||
msg->trx->nr, msg->lchan->ts->nr,
|
||||
gh->proto_discr & 0xf0,
|
||||
cc_msg_names[gh->msg_type & 0x3f]);
|
||||
else
|
||||
DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) "
|
||||
"Sending 0x%02x to MS.\n", msg->trx->bts->nr,
|
||||
msg->trx->nr, msg->lchan->ts->nr,
|
||||
gh->proto_discr, gh->msg_type);
|
||||
}
|
||||
|
||||
msg->l3h = msg->data;
|
||||
|
||||
return rsl_data_request(msg, 0);
|
||||
}
|
||||
|
||||
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
|
||||
int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
|
||||
{
|
||||
|
@ -1055,7 +898,7 @@ int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi)
|
|||
gsm0408_generate_lai(lai, bts->network->country_code,
|
||||
bts->network->network_code, bts->location_area_code);
|
||||
|
||||
mid = msgb_put(msg, MID_TMSI_LEN);
|
||||
mid = msgb_put(msg, GSM48_MID_TMSI_LEN);
|
||||
generate_mid_from_tmsi(mid, tmsi);
|
||||
|
||||
DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
|
||||
|
@ -1091,7 +934,7 @@ static int mi_to_string(char *string, int str_len, u_int8_t *mi, int mi_len)
|
|||
break;
|
||||
case GSM_MI_TYPE_TMSI:
|
||||
/* Table 10.5.4.3, reverse generate_mid_from_tmsi */
|
||||
if (mi_len == TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
|
||||
if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
|
||||
memcpy(&tmsi, &mi[1], 4);
|
||||
tmsi = ntohl(tmsi);
|
||||
return snprintf(string, str_len, "%u", tmsi);
|
||||
|
@ -1165,8 +1008,10 @@ static int mm_rx_id_resp(struct msgb *msg)
|
|||
case GSM_MI_TYPE_IMEI:
|
||||
case GSM_MI_TYPE_IMEISV:
|
||||
/* update subscribe <-> IMEI mapping */
|
||||
if (lchan->subscr)
|
||||
if (lchan->subscr) {
|
||||
db_subscriber_assoc_imei(lchan->subscr, mi_string);
|
||||
db_sync_equipment(&lchan->subscr->equipment);
|
||||
}
|
||||
if (lchan->loc_operation)
|
||||
lchan->loc_operation->waiting_for_imei = 0;
|
||||
break;
|
||||
|
@ -1289,6 +1134,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
|||
}
|
||||
|
||||
lchan->subscr = subscr;
|
||||
lchan->subscr->equipment.classmark1 = lu->classmark1;
|
||||
|
||||
/* check if we can let the subscriber into our network immediately
|
||||
* or if we need to wait for identity responses. */
|
||||
|
@ -1614,6 +1460,10 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
|
|||
GSM_SUBSCRIBER_UPDATE_DETACHED);
|
||||
DEBUGP(DMM, "Subscriber: %s\n",
|
||||
subscr->name ? subscr->name : subscr->imsi);
|
||||
|
||||
subscr->equipment.classmark1 = idi->classmark1;
|
||||
db_sync_equipment(&subscr->equipment);
|
||||
|
||||
subscr_put(subscr);
|
||||
} else
|
||||
DEBUGP(DMM, "Unknown Subscriber ?!?\n");
|
||||
|
@ -1825,6 +1675,24 @@ static int gsm48_rx_rr_meas_rep(struct msgb *msg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gsm48_rx_rr_app_info(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
u_int8_t apdu_id_flags;
|
||||
u_int8_t apdu_len;
|
||||
u_int8_t *apdu_data;
|
||||
|
||||
apdu_id_flags = gh->data[0];
|
||||
apdu_len = gh->data[1];
|
||||
apdu_data = gh->data+2;
|
||||
|
||||
DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s",
|
||||
apdu_id_flags, apdu_len, hexdump(apdu_data, apdu_len));
|
||||
|
||||
return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data);
|
||||
}
|
||||
|
||||
|
||||
/* Receive a GSM 04.08 Radio Resource (RR) message */
|
||||
static int gsm0408_rcv_rr(struct msgb *msg)
|
||||
{
|
||||
|
@ -1854,6 +1722,9 @@ static int gsm0408_rcv_rr(struct msgb *msg)
|
|||
case GSM48_MT_RR_MEAS_REP:
|
||||
rc = gsm48_rx_rr_meas_rep(msg);
|
||||
break;
|
||||
case GSM48_MT_RR_APP_INFO:
|
||||
rc = gsm48_rx_rr_app_info(msg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unimplemented GSM 04.08 RR msg type 0x%02x\n",
|
||||
gh->msg_type);
|
||||
|
@ -1863,29 +1734,26 @@ static int gsm0408_rcv_rr(struct msgb *msg)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* 7.1.7 and 9.1.7: RR CHANnel RELease */
|
||||
int gsm48_send_rr_release(struct gsm_lchan *lchan)
|
||||
|
||||
int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
|
||||
u_int8_t apdu_len, u_int8_t *apdu)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
u_int8_t *cause;
|
||||
struct gsm48_hdr *gh;
|
||||
|
||||
msg->lchan = lchan;
|
||||
|
||||
DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n",
|
||||
apdu_id, apdu_len);
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len);
|
||||
gh->proto_discr = GSM48_PDISC_RR;
|
||||
gh->msg_type = GSM48_MT_RR_CHAN_REL;
|
||||
gh->msg_type = GSM48_MT_RR_APP_INFO;
|
||||
gh->data[0] = apdu_id;
|
||||
gh->data[1] = apdu_len;
|
||||
memcpy(gh->data+2, apdu, apdu_len);
|
||||
|
||||
cause = msgb_put(msg, 1);
|
||||
cause[0] = GSM48_RR_CAUSE_NORMAL;
|
||||
|
||||
DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n",
|
||||
lchan->nr, lchan->type);
|
||||
|
||||
/* 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);
|
||||
return gsm48_sendmsg(msg, NULL);
|
||||
}
|
||||
|
||||
/* Call Control */
|
||||
|
@ -3707,7 +3575,7 @@ static int gsm0408_rcv_cc(struct msgb *msg)
|
|||
"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->cc.state):0,
|
||||
gsm0408_cc_msg_names[msg_type], trans?(trans->cc.state):0,
|
||||
cc_state_names[trans?(trans->cc.state):0]);
|
||||
|
||||
/* Create transaction */
|
||||
|
@ -3778,99 +3646,6 @@ int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Section 9.1.8 / Table 9.9 */
|
||||
struct chreq {
|
||||
u_int8_t val;
|
||||
u_int8_t mask;
|
||||
enum chreq_type type;
|
||||
};
|
||||
|
||||
/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */
|
||||
static const struct chreq chreq_type_neci1[] = {
|
||||
{ 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
|
||||
{ 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F },
|
||||
{ 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H },
|
||||
{ 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL },
|
||||
{ 0xe0, 0xe0, CHREQ_T_SDCCH },
|
||||
{ 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H },
|
||||
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
|
||||
{ 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
|
||||
{ 0x10, 0xf0, CHREQ_T_SDCCH },
|
||||
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
|
||||
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
|
||||
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
|
||||
};
|
||||
|
||||
/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
|
||||
static const struct chreq chreq_type_neci0[] = {
|
||||
{ 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
|
||||
{ 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H },
|
||||
{ 0xe0, 0xe0, CHREQ_T_TCH_F },
|
||||
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
|
||||
{ 0x00, 0xe0, CHREQ_T_LOCATION_UPD },
|
||||
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
|
||||
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
|
||||
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
|
||||
};
|
||||
|
||||
static const enum gsm_chan_t ctype_by_chreq[] = {
|
||||
[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_PAG_R_ANY] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F,
|
||||
};
|
||||
|
||||
static const enum gsm_chreq_reason_t reason_by_chreq[] = {
|
||||
[CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG,
|
||||
[CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL,
|
||||
[CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL,
|
||||
[CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL,
|
||||
[CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
|
||||
[CHREQ_T_PAG_R_ANY] = GSM_CHREQ_REASON_PAG,
|
||||
[CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG,
|
||||
[CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG,
|
||||
};
|
||||
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra)
|
||||
{
|
||||
int i;
|
||||
/* FIXME: determine if we set NECI = 0 in the BTS SI4 */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
|
||||
const struct chreq *chr = &chreq_type_neci0[i];
|
||||
if ((ra & chr->mask) == chr->val)
|
||||
return ctype_by_chreq[chr->type];
|
||||
}
|
||||
fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
|
||||
return GSM_LCHAN_SDCCH;
|
||||
}
|
||||
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra)
|
||||
{
|
||||
int i;
|
||||
/* FIXME: determine if we set NECI = 0 in the BTS SI4 */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
|
||||
const struct chreq *chr = &chreq_type_neci0[i];
|
||||
if ((ra & chr->mask) == chr->val)
|
||||
return reason_by_chreq[chr->type];
|
||||
}
|
||||
fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
|
||||
return GSM_CHREQ_REASON_OTHER;
|
||||
}
|
||||
|
||||
/* dequeue messages to layer 4 */
|
||||
int bsc_upqueue(struct gsm_network *net)
|
||||
{
|
||||
|
|
310
openbsc/src/gsm_04_08_utils.c
Normal file
310
openbsc/src/gsm_04_08_utils.c
Normal file
|
@ -0,0 +1,310 @@
|
|||
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
|
||||
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0
|
||||
* utility functions
|
||||
*/
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <openbsc/msgb.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/transaction.h>
|
||||
|
||||
#define GSM48_ALLOC_SIZE 1024
|
||||
#define GSM48_ALLOC_HEADROOM 128
|
||||
|
||||
/* should ip.access BTS use direct RTP streams between each other (1),
|
||||
* or should OpenBSC always act as RTP relay/proxy in between (0) ? */
|
||||
int ipacc_rtp_direct = 1;
|
||||
|
||||
|
||||
const char *gsm0408_cc_msg_names[] = {
|
||||
"unknown 0x00",
|
||||
"ALERTING",
|
||||
"CALL_PROC",
|
||||
"PROGRESS",
|
||||
"ESTAB",
|
||||
"SETUP",
|
||||
"ESTAB_CONF",
|
||||
"CONNECT",
|
||||
"CALL_CONF",
|
||||
"START_CC",
|
||||
"unknown 0x0a",
|
||||
"RECALL",
|
||||
"unknown 0x0c",
|
||||
"unknown 0x0d",
|
||||
"EMERG_SETUP",
|
||||
"CONNECT_ACK",
|
||||
"USER_INFO",
|
||||
"unknown 0x11",
|
||||
"unknown 0x12",
|
||||
"MODIFY_REJECT",
|
||||
"unknown 0x14",
|
||||
"unknown 0x15",
|
||||
"unknown 0x16",
|
||||
"MODIFY",
|
||||
"HOLD",
|
||||
"HOLD_ACK",
|
||||
"HOLD_REJ",
|
||||
"unknown 0x1b",
|
||||
"RETR",
|
||||
"RETR_ACK",
|
||||
"RETR_REJ",
|
||||
"MODIFY_COMPL",
|
||||
"unknown 0x20",
|
||||
"unknown 0x21",
|
||||
"unknown 0x22",
|
||||
"unknown 0x23",
|
||||
"unknown 0x24",
|
||||
"DISCONNECT",
|
||||
"unknown 0x26",
|
||||
"unknown 0x27",
|
||||
"unknown 0x28",
|
||||
"unknown 0x29",
|
||||
"RELEASE_COMPL",
|
||||
"unknown 0x2b",
|
||||
"unknown 0x2c",
|
||||
"RELEASE",
|
||||
"unknown 0x2e",
|
||||
"unknown 0x2f",
|
||||
"unknown 0x30",
|
||||
"STOP_DTMF",
|
||||
"STOP_DTMF_ACK",
|
||||
"unknown 0x33",
|
||||
"STATUS_ENQ",
|
||||
"START_DTMF",
|
||||
"START_DTMF_ACK",
|
||||
"START_DTMF_REJ",
|
||||
"unknown 0x38",
|
||||
"CONG_CTRL",
|
||||
"FACILITY",
|
||||
"unknown 0x3b",
|
||||
"STATUS",
|
||||
"unknown 0x3c",
|
||||
"NOTIFY",
|
||||
"unknown 0x3f",
|
||||
};
|
||||
|
||||
|
||||
struct msgb *gsm48_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM,
|
||||
"GSM 04.08");
|
||||
}
|
||||
|
||||
int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans)
|
||||
{
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data;
|
||||
|
||||
/* if we get passed a transaction reference, do some common
|
||||
* work that the caller no longer has to do */
|
||||
if (trans) {
|
||||
gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
|
||||
msg->lchan = trans->lchan;
|
||||
}
|
||||
|
||||
if (msg->lchan) {
|
||||
msg->trx = msg->lchan->ts->trx;
|
||||
|
||||
if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC)
|
||||
DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) "
|
||||
"Sending '%s' to MS.\n", msg->trx->bts->nr,
|
||||
msg->trx->nr, msg->lchan->ts->nr,
|
||||
gh->proto_discr & 0xf0,
|
||||
gsm0408_cc_msg_names[gh->msg_type & 0x3f]);
|
||||
else
|
||||
DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) "
|
||||
"Sending 0x%02x to MS.\n", msg->trx->bts->nr,
|
||||
msg->trx->nr, msg->lchan->ts->nr,
|
||||
gh->proto_discr, gh->msg_type);
|
||||
}
|
||||
|
||||
msg->l3h = msg->data;
|
||||
|
||||
return rsl_data_request(msg, 0);
|
||||
}
|
||||
|
||||
static void to_bcd(u_int8_t *bcd, u_int16_t val)
|
||||
{
|
||||
bcd[2] = val % 10;
|
||||
val = val / 10;
|
||||
bcd[1] = val % 10;
|
||||
val = val / 10;
|
||||
bcd[0] = val % 10;
|
||||
val = val / 10;
|
||||
}
|
||||
|
||||
void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc,
|
||||
u_int16_t mnc, u_int16_t lac)
|
||||
{
|
||||
u_int8_t bcd[3];
|
||||
|
||||
to_bcd(bcd, mcc);
|
||||
lai48->digits[0] = bcd[0] | (bcd[1] << 4);
|
||||
lai48->digits[1] = bcd[2];
|
||||
|
||||
to_bcd(bcd, mnc);
|
||||
/* FIXME: do we need three-digit MNC? See Table 10.5.3 */
|
||||
#if 0
|
||||
lai48->digits[1] |= bcd[2] << 4;
|
||||
lai48->digits[2] = bcd[0] | (bcd[1] << 4);
|
||||
#else
|
||||
lai48->digits[1] |= 0xf << 4;
|
||||
lai48->digits[2] = bcd[1] | (bcd[2] << 4);
|
||||
#endif
|
||||
|
||||
lai48->lac = htons(lac);
|
||||
}
|
||||
|
||||
int generate_mid_from_tmsi(u_int8_t *buf, u_int32_t tmsi)
|
||||
{
|
||||
u_int32_t *tptr = (u_int32_t *) &buf[3];
|
||||
|
||||
buf[0] = GSM48_IE_MOBILE_ID;
|
||||
buf[1] = GSM48_TMSI_LEN;
|
||||
buf[2] = 0xf0 | GSM_MI_TYPE_TMSI;
|
||||
*tptr = htonl(tmsi);
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
/* Section 9.1.8 / Table 9.9 */
|
||||
struct chreq {
|
||||
u_int8_t val;
|
||||
u_int8_t mask;
|
||||
enum chreq_type type;
|
||||
};
|
||||
|
||||
/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */
|
||||
static const struct chreq chreq_type_neci1[] = {
|
||||
{ 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
|
||||
{ 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F },
|
||||
{ 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H },
|
||||
{ 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL },
|
||||
{ 0xe0, 0xe0, CHREQ_T_SDCCH },
|
||||
{ 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H },
|
||||
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
|
||||
{ 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
|
||||
{ 0x10, 0xf0, CHREQ_T_SDCCH },
|
||||
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
|
||||
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
|
||||
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
|
||||
};
|
||||
|
||||
/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */
|
||||
static const struct chreq chreq_type_neci0[] = {
|
||||
{ 0xa0, 0xe0, CHREQ_T_EMERG_CALL },
|
||||
{ 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H },
|
||||
{ 0xe0, 0xe0, CHREQ_T_TCH_F },
|
||||
{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
|
||||
{ 0x00, 0xe0, CHREQ_T_LOCATION_UPD },
|
||||
{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
|
||||
{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
|
||||
{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
|
||||
};
|
||||
|
||||
static const enum gsm_chan_t ctype_by_chreq[] = {
|
||||
[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H,
|
||||
[CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_PAG_R_ANY] = GSM_LCHAN_SDCCH,
|
||||
[CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F,
|
||||
[CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F,
|
||||
};
|
||||
|
||||
static const enum gsm_chreq_reason_t reason_by_chreq[] = {
|
||||
[CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG,
|
||||
[CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL,
|
||||
[CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL,
|
||||
[CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL,
|
||||
[CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER,
|
||||
[CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD,
|
||||
[CHREQ_T_PAG_R_ANY] = GSM_CHREQ_REASON_PAG,
|
||||
[CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG,
|
||||
[CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG,
|
||||
};
|
||||
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra)
|
||||
{
|
||||
int i;
|
||||
/* FIXME: determine if we set NECI = 0 in the BTS SI4 */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
|
||||
const struct chreq *chr = &chreq_type_neci0[i];
|
||||
if ((ra & chr->mask) == chr->val)
|
||||
return ctype_by_chreq[chr->type];
|
||||
}
|
||||
fprintf(stderr, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
|
||||
return GSM_LCHAN_SDCCH;
|
||||
}
|
||||
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra)
|
||||
{
|
||||
int i;
|
||||
/* FIXME: determine if we set NECI = 0 in the BTS SI4 */
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
|
||||
const struct chreq *chr = &chreq_type_neci0[i];
|
||||
if ((ra & chr->mask) == chr->val)
|
||||
return reason_by_chreq[chr->type];
|
||||
}
|
||||
fprintf(stderr, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra);
|
||||
return GSM_CHREQ_REASON_OTHER;
|
||||
}
|
||||
|
||||
/* 7.1.7 and 9.1.7: RR CHANnel RELease */
|
||||
int gsm48_send_rr_release(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
|
||||
u_int8_t *cause;
|
||||
|
||||
msg->lchan = lchan;
|
||||
gh->proto_discr = GSM48_PDISC_RR;
|
||||
gh->msg_type = GSM48_MT_RR_CHAN_REL;
|
||||
|
||||
cause = msgb_put(msg, 1);
|
||||
cause[0] = GSM48_RR_CAUSE_NORMAL;
|
||||
|
||||
DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n",
|
||||
lchan->nr, lchan->type);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
@ -295,6 +295,9 @@ static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms)
|
|||
}
|
||||
/* dispatch a signal to tell higher level about it */
|
||||
dispatch_signal(SS_SMS, S_SMS_SUBMITTED, gsms);
|
||||
/* try delivering the SMS right now */
|
||||
//gsm411_send_sms_subscr(gsms->receiver, gsms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -675,14 +678,19 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
|
|||
|
||||
if (!trans->sms.is_mt) {
|
||||
DEBUGP(DSMS, "RX RP-ERR on a MO transfer ?\n");
|
||||
#if 0
|
||||
return gsm411_send_rp_error(trans, rph->msg_ref,
|
||||
GSM411_RP_CAUSE_MSG_INCOMP_STATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!sms) {
|
||||
DEBUGP(DSMS, "RX RP-ERR, but no sms in transaction?!?\n");
|
||||
return -EINVAL;
|
||||
#if 0
|
||||
return gsm411_send_rp_error(trans, rph->msg_ref,
|
||||
GSM411_RP_CAUSE_PROTOCOL_ERR);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */
|
||||
/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */
|
||||
|
||||
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
|
@ -27,101 +27,12 @@
|
|||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/db.h>
|
||||
|
||||
LLIST_HEAD(active_subscribers);
|
||||
void *tall_subscr_ctx;
|
||||
void *tall_sub_req_ctx;
|
||||
|
||||
/*
|
||||
* Struct for pending channel requests. This is managed in the
|
||||
* llist_head requests of each subscriber. The reference counting
|
||||
* should work in such a way that a subscriber with a pending request
|
||||
* remains in memory.
|
||||
*/
|
||||
struct subscr_request {
|
||||
struct llist_head entry;
|
||||
|
||||
/* back reference */
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* the requested channel type */
|
||||
int channel_type;
|
||||
|
||||
/* the bts we have decided to use */
|
||||
struct gsm_network *network;
|
||||
|
||||
/* the callback data */
|
||||
gsm_cbfn *cbfn;
|
||||
void *param;
|
||||
};
|
||||
|
||||
/*
|
||||
* We got the channel assigned and can now hand this channel
|
||||
* over to one of our callbacks.
|
||||
*/
|
||||
static int subscr_paging_cb(unsigned int hooknum, unsigned int event,
|
||||
struct msgb *msg, void *data, void *param)
|
||||
{
|
||||
struct subscr_request *request;
|
||||
struct gsm_subscriber *subscr = (struct gsm_subscriber *)param;
|
||||
|
||||
assert(!llist_empty(&subscr->requests));
|
||||
|
||||
/*
|
||||
* FIXME: What to do with paging requests coming during
|
||||
* this callback? We must be sure to not start paging when
|
||||
* we have an active connection to a subscriber and to make
|
||||
* the subscr_put_channel work as required...
|
||||
*/
|
||||
request = (struct subscr_request *)subscr->requests.next;
|
||||
llist_del(&request->entry);
|
||||
subscr->in_callback = 1;
|
||||
request->cbfn(hooknum, event, msg, data, request->param);
|
||||
subscr->in_callback = 0;
|
||||
|
||||
talloc_free(request);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void subscr_send_paging_request(struct gsm_subscriber *subscr)
|
||||
{
|
||||
struct subscr_request *request;
|
||||
assert(!llist_empty(&subscr->requests));
|
||||
|
||||
request = (struct subscr_request *)subscr->requests.next;
|
||||
paging_request(request->network, subscr, request->channel_type,
|
||||
subscr_paging_cb, subscr);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_alloc(void)
|
||||
{
|
||||
struct gsm_subscriber *s;
|
||||
|
||||
s = talloc(tall_subscr_ctx, struct gsm_subscriber);
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
llist_add_tail(&s->entry, &active_subscribers);
|
||||
s->use_count = 1;
|
||||
|
||||
INIT_LLIST_HEAD(&s->requests);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void subscr_free(struct gsm_subscriber *subscr)
|
||||
{
|
||||
llist_del(&subscr->entry);
|
||||
talloc_free(subscr);
|
||||
}
|
||||
extern struct llist_head *subscr_bsc_active_subscriber(void);
|
||||
|
||||
struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net,
|
||||
const char *tmsi)
|
||||
|
@ -129,7 +40,7 @@ struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net,
|
|||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* we might have a record in memory already */
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (strcmp(subscr->tmsi, tmsi) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
@ -142,7 +53,7 @@ struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net,
|
|||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (strcmp(subscr->imsi, imsi) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
@ -155,7 +66,7 @@ struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
|
|||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (strcmp(subscr->extension, ext) == 0)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
@ -170,7 +81,7 @@ struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net,
|
|||
char buf[32];
|
||||
sprintf(buf, "%llu", id);
|
||||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (subscr->id == id)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
@ -203,82 +114,4 @@ int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason)
|
|||
return db_sync_subscriber(s);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count++;
|
||||
DEBUGP(DCC, "subscr %s usage increases usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
return subscr;
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count--;
|
||||
DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
if (subscr->use_count <= 0)
|
||||
subscr_free(subscr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void subscr_get_channel(struct gsm_subscriber *subscr,
|
||||
struct gsm_network *network, int type,
|
||||
gsm_cbfn *cbfn, void *param)
|
||||
{
|
||||
struct subscr_request *request;
|
||||
|
||||
request = talloc(tall_sub_req_ctx, struct subscr_request);
|
||||
if (!request) {
|
||||
if (cbfn)
|
||||
cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM,
|
||||
NULL, NULL, param);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(request, 0, sizeof(*request));
|
||||
request->network = network;
|
||||
request->subscr = subscr;
|
||||
request->channel_type = type;
|
||||
request->cbfn = cbfn;
|
||||
request->param = param;
|
||||
|
||||
/*
|
||||
* FIXME: We might be able to assign more than one
|
||||
* channel, e.g. voice and SMS submit at the same
|
||||
* time.
|
||||
*/
|
||||
if (!subscr->in_callback && llist_empty(&subscr->requests)) {
|
||||
/* add to the list, send a request */
|
||||
llist_add_tail(&request->entry, &subscr->requests);
|
||||
subscr_send_paging_request(subscr);
|
||||
} else {
|
||||
/* this will be picked up later, from subscr_put_channel */
|
||||
llist_add_tail(&request->entry, &subscr->requests);
|
||||
}
|
||||
}
|
||||
|
||||
void subscr_put_channel(struct gsm_lchan *lchan)
|
||||
{
|
||||
/*
|
||||
* FIXME: Continue with other requests now... by checking
|
||||
* the gsm_subscriber inside the gsm_lchan. Drop the ref count
|
||||
* of the lchan after having asked the next requestee to handle
|
||||
* the channel.
|
||||
*/
|
||||
/*
|
||||
* FIXME: is the lchan is of a different type we could still
|
||||
* issue an immediate assignment for another channel and then
|
||||
* close this one.
|
||||
*/
|
||||
/*
|
||||
* Currently we will drop the last ref of the lchan which
|
||||
* will result in a channel release on RSL and we will start
|
||||
* the paging. This should work most of the time as the MS
|
||||
* will listen to the paging requests before we timeout
|
||||
*/
|
||||
|
||||
put_lchan(lchan);
|
||||
|
||||
if (lchan->subscr && !llist_empty(&lchan->subscr->requests))
|
||||
subscr_send_paging_request(lchan->subscr);
|
||||
}
|
||||
|
|
209
openbsc/src/gsm_subscriber_base.c
Normal file
209
openbsc/src/gsm_subscriber_base.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
/* The concept of a subscriber as seen by the BSC */
|
||||
|
||||
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <openbsc/talloc.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/paging.h>
|
||||
|
||||
LLIST_HEAD(active_subscribers);
|
||||
void *tall_subscr_ctx;
|
||||
void *tall_sub_req_ctx;
|
||||
|
||||
/* for the gsm_subscriber.c */
|
||||
struct llist_head *subscr_bsc_active_subscriber(void)
|
||||
{
|
||||
return &active_subscribers;
|
||||
}
|
||||
|
||||
/*
|
||||
* Struct for pending channel requests. This is managed in the
|
||||
* llist_head requests of each subscriber. The reference counting
|
||||
* should work in such a way that a subscriber with a pending request
|
||||
* remains in memory.
|
||||
*/
|
||||
struct subscr_request {
|
||||
struct llist_head entry;
|
||||
|
||||
/* back reference */
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* the requested channel type */
|
||||
int channel_type;
|
||||
|
||||
/* the bts we have decided to use */
|
||||
struct gsm_network *network;
|
||||
|
||||
/* the callback data */
|
||||
gsm_cbfn *cbfn;
|
||||
void *param;
|
||||
};
|
||||
|
||||
/*
|
||||
* We got the channel assigned and can now hand this channel
|
||||
* over to one of our callbacks.
|
||||
*/
|
||||
static int subscr_paging_cb(unsigned int hooknum, unsigned int event,
|
||||
struct msgb *msg, void *data, void *param)
|
||||
{
|
||||
struct subscr_request *request;
|
||||
struct gsm_subscriber *subscr = (struct gsm_subscriber *)param;
|
||||
|
||||
assert(!llist_empty(&subscr->requests));
|
||||
|
||||
/*
|
||||
* FIXME: What to do with paging requests coming during
|
||||
* this callback? We must be sure to not start paging when
|
||||
* we have an active connection to a subscriber and to make
|
||||
* the subscr_put_channel work as required...
|
||||
*/
|
||||
request = (struct subscr_request *)subscr->requests.next;
|
||||
llist_del(&request->entry);
|
||||
subscr->in_callback = 1;
|
||||
request->cbfn(hooknum, event, msg, data, request->param);
|
||||
subscr->in_callback = 0;
|
||||
|
||||
talloc_free(request);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void subscr_send_paging_request(struct gsm_subscriber *subscr)
|
||||
{
|
||||
struct subscr_request *request;
|
||||
assert(!llist_empty(&subscr->requests));
|
||||
|
||||
request = (struct subscr_request *)subscr->requests.next;
|
||||
paging_request(request->network, subscr, request->channel_type,
|
||||
subscr_paging_cb, subscr);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_alloc(void)
|
||||
{
|
||||
struct gsm_subscriber *s;
|
||||
|
||||
s = talloc(tall_subscr_ctx, struct gsm_subscriber);
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
llist_add_tail(&s->entry, &active_subscribers);
|
||||
s->use_count = 1;
|
||||
|
||||
INIT_LLIST_HEAD(&s->requests);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void subscr_free(struct gsm_subscriber *subscr)
|
||||
{
|
||||
llist_del(&subscr->entry);
|
||||
talloc_free(subscr);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count++;
|
||||
DEBUGP(DCC, "subscr %s usage increases usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
return subscr;
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr)
|
||||
{
|
||||
subscr->use_count--;
|
||||
DEBUGP(DCC, "subscr %s usage decreased usage to: %d\n",
|
||||
subscr->extension, subscr->use_count);
|
||||
if (subscr->use_count <= 0)
|
||||
subscr_free(subscr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void subscr_get_channel(struct gsm_subscriber *subscr,
|
||||
struct gsm_network *network, int type,
|
||||
gsm_cbfn *cbfn, void *param)
|
||||
{
|
||||
struct subscr_request *request;
|
||||
|
||||
request = talloc(tall_sub_req_ctx, struct subscr_request);
|
||||
if (!request) {
|
||||
if (cbfn)
|
||||
cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM,
|
||||
NULL, NULL, param);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(request, 0, sizeof(*request));
|
||||
request->network = network;
|
||||
request->subscr = subscr;
|
||||
request->channel_type = type;
|
||||
request->cbfn = cbfn;
|
||||
request->param = param;
|
||||
|
||||
/*
|
||||
* FIXME: We might be able to assign more than one
|
||||
* channel, e.g. voice and SMS submit at the same
|
||||
* time.
|
||||
*/
|
||||
if (!subscr->in_callback && llist_empty(&subscr->requests)) {
|
||||
/* add to the list, send a request */
|
||||
llist_add_tail(&request->entry, &subscr->requests);
|
||||
subscr_send_paging_request(subscr);
|
||||
} else {
|
||||
/* this will be picked up later, from subscr_put_channel */
|
||||
llist_add_tail(&request->entry, &subscr->requests);
|
||||
}
|
||||
}
|
||||
|
||||
void subscr_put_channel(struct gsm_lchan *lchan)
|
||||
{
|
||||
/*
|
||||
* FIXME: Continue with other requests now... by checking
|
||||
* the gsm_subscriber inside the gsm_lchan. Drop the ref count
|
||||
* of the lchan after having asked the next requestee to handle
|
||||
* the channel.
|
||||
*/
|
||||
/*
|
||||
* FIXME: is the lchan is of a different type we could still
|
||||
* issue an immediate assignment for another channel and then
|
||||
* close this one.
|
||||
*/
|
||||
/*
|
||||
* Currently we will drop the last ref of the lchan which
|
||||
* will result in a channel release on RSL and we will start
|
||||
* the paging. This should work most of the time as the MS
|
||||
* will listen to the paging requests before we timeout
|
||||
*/
|
||||
|
||||
put_lchan(lchan);
|
||||
|
||||
if (lchan->subscr && !llist_empty(&lchan->subscr->requests))
|
||||
subscr_send_paging_request(lchan->subscr);
|
||||
}
|
||||
|
73
openbsc/src/rrlp.c
Normal file
73
openbsc/src/rrlp.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
|
||||
|
||||
/* (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 <sys/types.h>
|
||||
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
|
||||
/* RRLP MS based position request */
|
||||
static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 };
|
||||
|
||||
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;
|
||||
|
||||
switch (signal) {
|
||||
case S_SUBSCR_ATTACHED:
|
||||
/* A subscriber has attached. */
|
||||
subscr = signal_data;
|
||||
lchan = lchan_for_subscr(subscr);
|
||||
if (!lchan)
|
||||
break;
|
||||
gsm48_send_rr_app_info(lchan, 0x00, sizeof(ms_based_pos_req),
|
||||
ms_based_pos_req);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int paging_sig_cb(unsigned int subsys, unsigned int signal,
|
||||
void *handler_data, void *signal_data)
|
||||
{
|
||||
struct paging_signal_data *psig_data = signal_data;
|
||||
|
||||
switch (signal) {
|
||||
case S_PAGING_COMPLETED:
|
||||
/* A subscriber has attached. */
|
||||
gsm48_send_rr_app_info(psig_data->lchan, 0x00,
|
||||
sizeof(ms_based_pos_req),
|
||||
ms_based_pos_req);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void on_dso_load_rrlp(void)
|
||||
{
|
||||
register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL);
|
||||
register_signal_handler(SS_PAGING, paging_sig_cb, NULL);
|
||||
}
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include <openbsc/linuxlist.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/gsm_04_11.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/gsm_utils.h>
|
||||
|
@ -64,12 +62,6 @@ struct cmd_node ts_node = {
|
|||
1,
|
||||
};
|
||||
|
||||
struct cmd_node subscr_node = {
|
||||
SUBSCR_NODE,
|
||||
"%s(subscriber)#",
|
||||
1,
|
||||
};
|
||||
|
||||
static int dummy_config_write(struct vty *v)
|
||||
{
|
||||
return CMD_SUCCESS;
|
||||
|
@ -421,7 +413,7 @@ DEFUN(show_ts,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
|
||||
void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr)
|
||||
{
|
||||
vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id,
|
||||
subscr->authorized, VTY_NEWLINE);
|
||||
|
@ -704,28 +696,6 @@ DEFUN(show_paging,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* per-subscriber configuration */
|
||||
DEFUN(cfg_subscr,
|
||||
cfg_subscr_cmd,
|
||||
"subscriber IMSI",
|
||||
"Select a Subscriber to configure\n")
|
||||
{
|
||||
const char *imsi = argv[0];
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
subscr = subscr_get_by_imsi(gsmnet, imsi);
|
||||
if (!subscr) {
|
||||
vty_out(vty, "%% No subscriber for IMSI %s%s",
|
||||
imsi, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->index = subscr;
|
||||
vty->node = SUBSCR_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_net,
|
||||
cfg_net_cmd,
|
||||
"network",
|
||||
|
@ -1158,220 +1128,6 @@ DEFUN(cfg_ts_e1_subslot,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Subscriber */
|
||||
DEFUN(show_subscr,
|
||||
show_subscr_cmd,
|
||||
"show subscriber [IMSI]",
|
||||
SHOW_STR "Display information about a subscriber\n")
|
||||
{
|
||||
const char *imsi;
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
if (argc >= 1) {
|
||||
imsi = argv[0];
|
||||
subscr = subscr_get_by_imsi(gsmnet, imsi);
|
||||
if (!subscr) {
|
||||
vty_out(vty, "%% unknown subscriber%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
subscr_dump_vty(vty, subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: iterate over all subscribers ? */
|
||||
return CMD_WARNING;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_subscr_cache,
|
||||
show_subscr_cache_cmd,
|
||||
"show subscriber cache",
|
||||
SHOW_STR "Display contents of subscriber cache\n")
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
|
||||
subscr_dump_vty(vty, 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;
|
||||
int id = atoi(argv[0]);
|
||||
|
||||
while (1) {
|
||||
sms = db_sms_get_unsent(gsmnet, id++);
|
||||
if (!sms)
|
||||
return CMD_WARNING;
|
||||
|
||||
if (!sms->receiver) {
|
||||
sms_free(sms);
|
||||
continue;
|
||||
}
|
||||
|
||||
gsm411_send_sms_subscr(sms->receiver, sms);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, const char *text)
|
||||
{
|
||||
struct gsm_sms *sms = sms_alloc();
|
||||
|
||||
if (!sms)
|
||||
return NULL;
|
||||
|
||||
if (!receiver->lac) {
|
||||
/* subscriber currently not attached, store in database? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sms->receiver = subscr_get(receiver);
|
||||
strncpy(sms->text, text, 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);
|
||||
|
||||
return sms;
|
||||
}
|
||||
|
||||
static int _send_sms_buffer(struct gsm_subscriber *receiver,
|
||||
struct buffer *b)
|
||||
{
|
||||
struct gsm_sms *sms;
|
||||
|
||||
sms = sms_from_text(receiver, buffer_getstr(b));
|
||||
|
||||
gsm411_send_sms_subscr(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_subscriber *receiver;
|
||||
struct buffer *b;
|
||||
int rc;
|
||||
|
||||
receiver = subscr_get_by_extension(gsmnet, argv[0]);
|
||||
if (!receiver)
|
||||
return CMD_WARNING;
|
||||
|
||||
b = argv_to_buffer(argc, argv, 1);
|
||||
rc = _send_sms_buffer(receiver, b);
|
||||
buffer_free(b);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
DEFUN(sms_send_imsi,
|
||||
sms_send_imsi_cmd,
|
||||
"sms send imsi IMSI .LINE",
|
||||
"Send a message to a subscriber identified by IMSI")
|
||||
{
|
||||
struct gsm_subscriber *receiver;
|
||||
struct buffer *b;
|
||||
int rc;
|
||||
|
||||
receiver = subscr_get_by_imsi(gsmnet, argv[0]);
|
||||
if (!receiver)
|
||||
return CMD_WARNING;
|
||||
|
||||
b = argv_to_buffer(argc, argv, 1);
|
||||
rc = _send_sms_buffer(receiver, b);
|
||||
buffer_free(b);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_subscr_name,
|
||||
cfg_subscr_name_cmd,
|
||||
"name NAME",
|
||||
"Set the name of the subscriber")
|
||||
{
|
||||
const char *name = argv[0];
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
strncpy(subscr->name, name, sizeof(subscr->name));
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_subscr_extension,
|
||||
cfg_subscr_extension_cmd,
|
||||
"extension EXTENSION",
|
||||
"Set the extension of the subscriber")
|
||||
{
|
||||
const char *name = argv[0];
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
strncpy(subscr->extension, name, sizeof(subscr->extension));
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_subscr_authorized,
|
||||
cfg_subscr_authorized_cmd,
|
||||
"auth <0-1>",
|
||||
"Set the authorization status of the subscriber")
|
||||
{
|
||||
int auth = atoi(argv[0]);
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
if (auth)
|
||||
subscr->authorized = 1;
|
||||
else
|
||||
subscr->authorized = 0;
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int bsc_vty_init(struct gsm_network *net)
|
||||
{
|
||||
gsmnet = net;
|
||||
|
@ -1391,13 +1147,6 @@ int bsc_vty_init(struct gsm_network *net)
|
|||
|
||||
install_element(VIEW_NODE, &show_paging_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &show_subscr_cmd);
|
||||
install_element(VIEW_NODE, &show_subscr_cache_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &sms_send_pend_cmd);
|
||||
install_element(VIEW_NODE, &sms_send_ext_cmd);
|
||||
install_element(VIEW_NODE, &sms_send_imsi_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_net_cmd);
|
||||
install_node(&net_node, config_write_net);
|
||||
install_default(GSMNET_NODE);
|
||||
|
@ -1438,12 +1187,7 @@ int bsc_vty_init(struct gsm_network *net)
|
|||
install_element(TS_NODE, &cfg_ts_pchan_cmd);
|
||||
install_element(TS_NODE, &cfg_ts_e1_subslot_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_subscr_cmd);
|
||||
install_node(&subscr_node, dummy_config_write);
|
||||
install_default(SUBSCR_NODE);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_name_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd);
|
||||
bsc_vty_init_extra(net);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
315
openbsc/src/vty_interface_layer3.c
Normal file
315
openbsc/src/vty_interface_layer3.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
/* OpenBSC interface to quagga VTY */
|
||||
/* (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 <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <vty/command.h>
|
||||
#include <vty/buffer.h>
|
||||
#include <vty/vty.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <openbsc/linuxlist.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/gsm_04_11.h>
|
||||
#include <openbsc/e1_input.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/gsm_utils.h>
|
||||
#include <openbsc/db.h>
|
||||
#include <openbsc/talloc.h>
|
||||
|
||||
/* forward declarations */
|
||||
void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr);
|
||||
|
||||
static struct gsm_network *gsmnet;
|
||||
|
||||
struct cmd_node subscr_node = {
|
||||
SUBSCR_NODE,
|
||||
"%s(subscriber)#",
|
||||
1,
|
||||
};
|
||||
|
||||
static int dummy_config_write(struct vty *v)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* per-subscriber configuration */
|
||||
DEFUN(cfg_subscr,
|
||||
cfg_subscr_cmd,
|
||||
"subscriber IMSI",
|
||||
"Select a Subscriber to configure\n")
|
||||
{
|
||||
const char *imsi = argv[0];
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
subscr = subscr_get_by_imsi(gsmnet, imsi);
|
||||
if (!subscr) {
|
||||
vty_out(vty, "%% No subscriber for IMSI %s%s",
|
||||
imsi, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty->index = subscr;
|
||||
vty->node = SUBSCR_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Subscriber */
|
||||
DEFUN(show_subscr,
|
||||
show_subscr_cmd,
|
||||
"show subscriber [IMSI]",
|
||||
SHOW_STR "Display information about a subscriber\n")
|
||||
{
|
||||
const char *imsi;
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
if (argc >= 1) {
|
||||
imsi = argv[0];
|
||||
subscr = subscr_get_by_imsi(gsmnet, imsi);
|
||||
if (!subscr) {
|
||||
vty_out(vty, "%% unknown subscriber%s",
|
||||
VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
subscr_dump_vty(vty, subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: iterate over all subscribers ? */
|
||||
return CMD_WARNING;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_subscr_cache,
|
||||
show_subscr_cache_cmd,
|
||||
"show subscriber cache",
|
||||
SHOW_STR "Display contents of subscriber cache\n")
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, &active_subscribers, entry) {
|
||||
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
|
||||
subscr_dump_vty(vty, 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;
|
||||
int id = atoi(argv[0]);
|
||||
|
||||
while (1) {
|
||||
sms = db_sms_get_unsent(gsmnet, id++);
|
||||
if (!sms)
|
||||
return CMD_WARNING;
|
||||
|
||||
if (!sms->receiver) {
|
||||
sms_free(sms);
|
||||
continue;
|
||||
}
|
||||
|
||||
gsm411_send_sms_subscr(sms->receiver, sms);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, const char *text)
|
||||
{
|
||||
struct gsm_sms *sms = sms_alloc();
|
||||
|
||||
if (!sms)
|
||||
return NULL;
|
||||
|
||||
if (!receiver->lac) {
|
||||
/* subscriber currently not attached, store in database? */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sms->receiver = subscr_get(receiver);
|
||||
strncpy(sms->text, text, 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);
|
||||
|
||||
return sms;
|
||||
}
|
||||
|
||||
static int _send_sms_buffer(struct gsm_subscriber *receiver,
|
||||
struct buffer *b)
|
||||
{
|
||||
struct gsm_sms *sms;
|
||||
|
||||
sms = sms_from_text(receiver, buffer_getstr(b));
|
||||
|
||||
gsm411_send_sms_subscr(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_subscriber *receiver;
|
||||
struct buffer *b;
|
||||
int rc;
|
||||
|
||||
receiver = subscr_get_by_extension(gsmnet, argv[0]);
|
||||
if (!receiver)
|
||||
return CMD_WARNING;
|
||||
|
||||
b = argv_to_buffer(argc, argv, 1);
|
||||
rc = _send_sms_buffer(receiver, b);
|
||||
buffer_free(b);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
DEFUN(sms_send_imsi,
|
||||
sms_send_imsi_cmd,
|
||||
"sms send imsi IMSI .LINE",
|
||||
"Send a message to a subscriber identified by IMSI")
|
||||
{
|
||||
struct gsm_subscriber *receiver;
|
||||
struct buffer *b;
|
||||
int rc;
|
||||
|
||||
receiver = subscr_get_by_imsi(gsmnet, argv[0]);
|
||||
if (!receiver)
|
||||
return CMD_WARNING;
|
||||
|
||||
b = argv_to_buffer(argc, argv, 1);
|
||||
rc = _send_sms_buffer(receiver, b);
|
||||
buffer_free(b);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_subscr_name,
|
||||
cfg_subscr_name_cmd,
|
||||
"name NAME",
|
||||
"Set the name of the subscriber")
|
||||
{
|
||||
const char *name = argv[0];
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
strncpy(subscr->name, name, sizeof(subscr->name));
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_subscr_extension,
|
||||
cfg_subscr_extension_cmd,
|
||||
"extension EXTENSION",
|
||||
"Set the extension of the subscriber")
|
||||
{
|
||||
const char *name = argv[0];
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
strncpy(subscr->extension, name, sizeof(subscr->extension));
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_subscr_authorized,
|
||||
cfg_subscr_authorized_cmd,
|
||||
"auth <0-1>",
|
||||
"Set the authorization status of the subscriber")
|
||||
{
|
||||
int auth = atoi(argv[0]);
|
||||
struct gsm_subscriber *subscr = vty->index;
|
||||
|
||||
if (auth)
|
||||
subscr->authorized = 1;
|
||||
else
|
||||
subscr->authorized = 0;
|
||||
|
||||
db_sync_subscriber(subscr);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int bsc_vty_init_extra(struct gsm_network *net)
|
||||
{
|
||||
gsmnet = net;
|
||||
|
||||
install_element(VIEW_NODE, &show_subscr_cmd);
|
||||
install_element(VIEW_NODE, &show_subscr_cache_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &sms_send_pend_cmd);
|
||||
install_element(VIEW_NODE, &sms_send_ext_cmd);
|
||||
install_element(VIEW_NODE, &sms_send_imsi_cmd);
|
||||
|
||||
install_element(CONFIG_NODE, &cfg_subscr_cmd);
|
||||
install_node(&subscr_node, dummy_config_write);
|
||||
|
||||
install_default(SUBSCR_NODE);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_name_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_extension_cmd);
|
||||
install_element(SUBSCR_NODE, &cfg_subscr_authorized_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -5,6 +5,7 @@ noinst_PROGRAMS = channel_test
|
|||
|
||||
channel_test_SOURCES = channel_test.c \
|
||||
$(top_srcdir)/src/db.c \
|
||||
$(top_srcdir)/src/gsm_subscriber_base.c \
|
||||
$(top_srcdir)/src/gsm_subscriber.c \
|
||||
$(top_srcdir)/src/debug.c \
|
||||
$(top_srcdir)/src/timer.c \
|
||||
|
|
|
@ -4,5 +4,5 @@ AM_CFLAGS=-Wall -ggdb3
|
|||
noinst_PROGRAMS = db_test
|
||||
|
||||
db_test_SOURCES = db_test.c
|
||||
db_test_LDADD = $(top_builddir)/src/libbsc.a -ldl -ldbi
|
||||
db_test_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a $(top_builddir)/src/libbsc.a -ldl -ldbi
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
|||
noinst_PROGRAMS = gsm0408_test
|
||||
|
||||
gsm0408_test_SOURCES = gsm0408_test.c
|
||||
gsm0408_test_LDADD = $(top_builddir)/src/libbsc.a -ldbi
|
||||
gsm0408_test_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a $(top_builddir)/src/libbsc.a -ldbi
|
||||
|
|
|
@ -2,4 +2,4 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
|||
noinst_PROGRAMS = sms_test
|
||||
|
||||
sms_test_SOURCES = sms_test.c
|
||||
sms_test_LDADD = $(top_builddir)/src/libbsc.a -ldl -ldbi
|
||||
sms_test_LDADD = $(top_builddir)/src/libmsc.a $(top_builddir)/src/libbsc.a -ldl -ldbi
|
||||
|
|
Loading…
Reference in a new issue