ussd: Send USSD on call setup on MSC errors

Send an USSD message to the mobile station requesting a connection
for a call or a SMS when the link to the MSC is down or in the
grace period.

The messages can be set (and this feature activated) by setting
bsc/missing-msc-text resp. msc/bsc-grace-text via the vty.

The generation of both messages has been tested manually.

Ticket: OW#957
This commit is contained in:
Jacob Erlbeck 2013-09-11 10:46:55 +02:00 committed by Holger Hans Peter Freyther
parent 323d2f5a47
commit 3ccb86bedc
6 changed files with 184 additions and 13 deletions

View File

@ -7,6 +7,13 @@
#define BSS_SEND_USSD 1
enum bsc_con {
BSC_CON_SUCCESS,
BSC_CON_REJECT_NO_LINK,
BSC_CON_REJECT_RF_GRACE,
BSC_CON_NO_MEM,
};
struct sccp_connection;
struct osmo_msc_data;
struct bsc_msc_connection;
@ -34,8 +41,8 @@ struct bsc_api *osmo_bsc_api();
int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg);
int bsc_open_connection(struct osmo_bsc_sccp_con *sccp, struct msgb *msg);
int bsc_create_new_connection(struct gsm_subscriber_connection *conn,
struct osmo_msc_data *msc);
enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn,
struct osmo_msc_data *msc);
int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp);
struct osmo_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *);

View File

@ -86,6 +86,9 @@ struct osmo_msc_data {
/* ussd msc connection lost text */
char *ussd_msc_lost_txt;
/* ussd text when MSC has entered the grace period */
char *ussd_grace_txt;
};
/*
@ -103,6 +106,9 @@ struct osmo_bsc_data {
char *rf_ctrl_name;
struct osmo_bsc_rf *rf_ctrl;
int auto_off_timeout;
/* ussd text when there is no MSC available */
char *ussd_no_msc_txt;
};

View File

@ -21,6 +21,8 @@
#include <openbsc/osmo_msc_data.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_04_80.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
@ -85,6 +87,47 @@ static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
queue_msg_or_return(resp);
}
static void bsc_send_ussd_notification(struct gsm_subscriber_connection *conn,
struct msgb *msg, const char *text)
{
struct gsm48_hdr *gh;
int8_t pdisc;
uint8_t mtype;
int drop_message = 1;
if (!text)
return;
if (!msg || msgb_l3len(msg) < sizeof(*gh))
return;
gh = msgb_l3(msg);
pdisc = gh->proto_discr & 0x0f;
mtype = gh->msg_type & 0xbf;
/* Is CM service request? */
if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) {
struct gsm48_service_request *cm;
cm = (struct gsm48_service_request *) &gh->data[0];
/* Is type SMS or call? */
if (cm->cm_service_type == GSM48_CMSERV_SMS)
drop_message = 0;
else if (cm->cm_service_type == GSM48_CMSERV_MO_CALL_PACKET)
drop_message = 0;
}
if (drop_message) {
LOGP(DMSC, LOGL_DEBUG, "Skipping (not sending) USSD message: '%s'\n", text);
return;
}
LOGP(DMSC, LOGL_INFO, "Sending USSD message: '%s'\n", text);
gsm0480_send_ussdNotify(conn, 1, text);
gsm0480_send_releaseComplete(conn);
}
/*
* Instruct to reserve data for a new connectiom, create the complete
* layer three message, send it to open the connection.
@ -100,6 +143,7 @@ static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg
msc = bsc_find_msc(conn, msg);
if (!msc) {
LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n");
bsc_send_ussd_notification(conn, msg, conn->bts->network->bsc_data->ussd_no_msc_txt);
return -1;
}
@ -112,10 +156,22 @@ static int complete_layer3(struct gsm_subscriber_connection *conn,
struct msgb *resp;
uint16_t network_code;
uint16_t country_code;
enum bsc_con ret;
/* allocate resource for a new connection */
if (bsc_create_new_connection(conn, msc) != 0)
ret = bsc_create_new_connection(conn, msc);
if (ret != BSC_CON_SUCCESS) {
/* allocation has failed */
if (ret == BSC_CON_REJECT_NO_LINK)
bsc_send_ussd_notification(conn, msg, msc->ussd_msc_lost_txt);
else if (ret == BSC_CON_REJECT_RF_GRACE)
bsc_send_ussd_notification(conn, msg, msc->ussd_grace_txt);
return BSC_API_CONN_POL_REJECT;
}
/* check return value, if failed check msg for and send USSD */
network_code = get_network_code_for_msc(conn->sccp_con->msc);
country_code = get_country_code_for_msc(conn->sccp_con->msc);

View File

@ -188,35 +188,35 @@ int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
return 0;
}
int bsc_create_new_connection(struct gsm_subscriber_connection *conn,
enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn,
struct osmo_msc_data *msc)
{
struct osmo_bsc_sccp_con *bsc_con;
struct sccp_connection *sccp;
/* This should not trigger */
if (!msc->msc_con->is_authenticated) {
if (!msc || !msc->msc_con->is_authenticated) {
LOGP(DMSC, LOGL_ERROR,
"How did this happen? MSC is not connected. Dropping.\n");
return -1;
return BSC_CON_REJECT_NO_LINK;
}
if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) {
LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
return -1;
return BSC_CON_REJECT_RF_GRACE;
}
sccp = sccp_connection_socket();
if (!sccp) {
LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n");
return -ENOMEM;
return BSC_CON_NO_MEM;
}
bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con);
if (!bsc_con) {
LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n");
sccp_connection_free(sccp);
return -1;
return BSC_CON_NO_MEM;
}
/* callbacks */
@ -237,7 +237,7 @@ int bsc_create_new_connection(struct gsm_subscriber_connection *conn,
bsc_con->conn = conn;
llist_add_tail(&bsc_con->entry, &active_connections);
conn->sccp_con = bsc_con;
return 0;
return BSC_CON_SUCCESS;
}
int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg)

View File

@ -122,6 +122,11 @@ static void write_msc(struct vty *vty, struct osmo_msc_data *msc)
else
vty_out(vty, " no bsc-msc-lost-text%s", VTY_NEWLINE);
if (msc->ussd_grace_txt && msc->ussd_grace_txt[0])
vty_out(vty, " bsc-grace-text %s%s", msc->ussd_grace_txt, VTY_NEWLINE);
else
vty_out(vty, " no bsc-grace-text%s", VTY_NEWLINE);
if (msc->audio_length != 0) {
int i;
@ -182,6 +187,11 @@ static int config_write_bsc(struct vty *vty)
vty_out(vty, " bsc-auto-rf-off %d%s",
bsc->auto_off_timeout, VTY_NEWLINE);
if (bsc->ussd_no_msc_txt && bsc->ussd_no_msc_txt[0])
vty_out(vty, " missing-msc-text %s%s", bsc->ussd_no_msc_txt, VTY_NEWLINE);
else
vty_out(vty, " no missing-msc-text%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@ -384,7 +394,7 @@ DEFUN(cfg_net_msc_no_welcome_ussd,
struct osmo_msc_data *data = osmo_msc_data(vty);
talloc_free(data->ussd_welcome_txt);
data->ussd_welcome_txt = 0;
data->ussd_welcome_txt = NULL;
return CMD_SUCCESS;
}
@ -417,6 +427,63 @@ DEFUN(cfg_net_msc_no_lost_ussd,
return CMD_SUCCESS;
}
DEFUN(cfg_net_msc_grace_ussd,
cfg_net_msc_grace_ussd_cmd,
"bsc-grace-text .TEXT",
"Set the USSD notification to be sent when the MSC has entered the grace period\n" "Text to be sent\n")
{
struct osmo_msc_data *data = osmo_msc_data(vty);
char *str = argv_concat(argv, argc, 0);
if (!str)
return CMD_WARNING;
bsc_replace_string(osmo_bsc_data(vty), &data->ussd_grace_txt, str);
talloc_free(str);
return CMD_SUCCESS;
}
DEFUN(cfg_net_msc_no_grace_ussd,
cfg_net_msc_no_grace_ussd_cmd,
"no bsc-grace-text",
NO_STR "Clear the USSD notification to be sent when the MSC has entered the grace period\n")
{
struct osmo_msc_data *data = osmo_msc_data(vty);
talloc_free(data->ussd_grace_txt);
data->ussd_grace_txt = NULL;
return CMD_SUCCESS;
}
DEFUN(cfg_net_bsc_missing_msc_ussd,
cfg_net_bsc_missing_msc_ussd_cmd,
"missing-msc-text .TEXT",
"Set the USSD notification to be send when a MSC has not been found.\n" "Text to be sent\n")
{
struct osmo_bsc_data *data = osmo_bsc_data(vty);
char *txt = argv_concat(argv, argc, 0);
if (!txt)
return CMD_WARNING;
bsc_replace_string(data, &data->ussd_no_msc_txt, txt);
talloc_free(txt);
return CMD_SUCCESS;
}
DEFUN(cfg_net_bsc_no_missing_msc_text,
cfg_net_bsc_no_missing_msc_text_cmd,
"no missing-msc-text",
NO_STR "Clear the USSD notification to be send when a MSC has not been found.\n")
{
struct osmo_bsc_data *data = osmo_bsc_data(vty);
talloc_free(data->ussd_no_msc_txt);
data->ussd_no_msc_txt = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_net_msc_type,
cfg_net_msc_type_cmd,
"type (normal|local)",
@ -615,6 +682,8 @@ int bsc_vty_init_extra(void)
install_element(BSC_NODE, &cfg_net_rf_socket_cmd);
install_element(BSC_NODE, &cfg_net_rf_off_time_cmd);
install_element(BSC_NODE, &cfg_net_no_rf_off_time_cmd);
install_element(BSC_NODE, &cfg_net_bsc_missing_msc_ussd_cmd);
install_element(BSC_NODE, &cfg_net_bsc_no_missing_msc_text_cmd);
install_node(&msc_node, config_write_msc);
bsc_install_default(MSC_NODE);
@ -631,6 +700,8 @@ int bsc_vty_init_extra(void)
install_element(MSC_NODE, &cfg_net_msc_no_welcome_ussd_cmd);
install_element(MSC_NODE, &cfg_net_msc_lost_ussd_cmd);
install_element(MSC_NODE, &cfg_net_msc_no_lost_ussd_cmd);
install_element(MSC_NODE, &cfg_net_msc_grace_ussd_cmd);
install_element(MSC_NODE, &cfg_net_msc_no_grace_ussd_cmd);
install_element(MSC_NODE, &cfg_net_msc_type_cmd);
install_element(MSC_NODE, &cfg_net_msc_emerg_cmd);
install_element(MSC_NODE, &cfg_net_msc_local_prefix_cmd);

View File

@ -207,7 +207,7 @@ class TestVTYBSC(TestVTYGenericBSC):
self.vty.command("msc 0")
self.assertEquals(self.vty.node(), 'config-msc')
def testUssdNotifications(self):
def testUssdNotificationsMsc(self):
self.vty.enable()
self.vty.command("configure terminal")
self.vty.command("msc")
@ -215,10 +215,12 @@ class TestVTYBSC(TestVTYGenericBSC):
# Test invalid input
self.vty.verify("bsc-msc-lost-text", ['% Command incomplete.'])
self.vty.verify("bsc-welcome-text", ['% Command incomplete.'])
self.vty.verify("bsc-grace-text", ['% Command incomplete.'])
# Enable USSD notifications
self.vty.verify("bsc-msc-lost-text MSC disconnected", [''])
self.vty.verify("bsc-welcome-text Hello MS", [''])
self.vty.verify("bsc-grace-text In grace period", [''])
# Verify settings
res = self.vty.command("write terminal")
@ -226,17 +228,46 @@ class TestVTYBSC(TestVTYGenericBSC):
self.assertEquals(res.find('no bsc-msc-lost-text'), -1)
self.assert_(res.find('bsc-welcome-text Hello MS') > 0)
self.assertEquals(res.find('no bsc-welcome-text'), -1)
self.assert_(res.find('bsc-grace-text In grace period') > 0)
self.assertEquals(res.find('no bsc-grace-text'), -1)
# Now disable it..
self.vty.verify("no bsc-msc-lost-text", [''])
self.vty.verify("no bsc-welcome-text", [''])
self.vty.verify("no bsc-grace-text", [''])
# Verify settings
res = self.vty.command("write terminal")
self.assertEquals(res.find('bsc-msc-lost-text MSC disconnected'), -1)
self.assert_(res.find('no bsc-msc-lost-text') > 0)
self.assert_(res.find('no bsc-welcome-text') > 0)
self.assertEquals(res.find('bsc-welcome-text Hello MS'), -1)
self.assert_(res.find('no bsc-welcome-text') > 0)
self.assertEquals(res.find('bsc-grace-text In grace period'), -1)
self.assert_(res.find('no bsc-grace-text') > 0)
def testUssdNotificationsBsc(self):
self.vty.enable()
self.vty.command("configure terminal")
self.vty.command("bsc")
# Test invalid input
self.vty.verify("missing-msc-text", ['% Command incomplete.'])
# Enable USSD notifications
self.vty.verify("missing-msc-text No MSC found", [''])
# Verify settings
res = self.vty.command("write terminal")
self.assert_(res.find('missing-msc-text No MSC found') > 0)
self.assertEquals(res.find('no missing-msc-text'), -1)
# Now disable it..
self.vty.verify("no missing-msc-text", [''])
# Verify settings
res = self.vty.command("write terminal")
self.assertEquals(res.find('missing-msc-text No MSC found'), -1)
self.assert_(res.find('no missing-msc-text') > 0)
class TestVTYNAT(TestVTYGenericBSC):