339 lines
11 KiB
C
339 lines
11 KiB
C
/*
|
|
* (C) 2018-2019 by Vadim Yanitskiy <axilirator@gmail.com>
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include <osmocom/core/linuxlist.h>
|
|
#include <osmocom/core/utils.h>
|
|
#include <osmocom/core/msgb.h>
|
|
|
|
#include <osmocom/gsupclient/gsup_client.h>
|
|
#include <osmocom/msc/gsm_subscriber.h>
|
|
#include <osmocom/msc/transaction.h>
|
|
#include <osmocom/msc/msc_common.h>
|
|
#include <osmocom/msc/debug.h>
|
|
#include <osmocom/msc/vlr.h>
|
|
#include <osmocom/msc/msub.h>
|
|
#include <osmocom/msc/gsup_client_mux.h>
|
|
#include <osmocom/msc/msc_a.h>
|
|
|
|
/* Common helper for preparing to be encoded GSUP message */
|
|
static void gsup_sm_msg_init(struct osmo_gsup_message *gsup_msg,
|
|
enum osmo_gsup_message_type msg_type, const char *imsi,
|
|
uint8_t *sm_rp_mr)
|
|
{
|
|
/* Init a mew GSUP message */
|
|
*gsup_msg = (struct osmo_gsup_message){
|
|
.message_type = msg_type,
|
|
.sm_rp_mr = sm_rp_mr,
|
|
.message_class = OSMO_GSUP_MESSAGE_CLASS_SMS,
|
|
};
|
|
|
|
/* Fill in subscriber's IMSI */
|
|
OSMO_STRLCPY_ARRAY(gsup_msg->imsi, imsi);
|
|
}
|
|
|
|
int gsm411_gsup_mo_fwd_sm_req(struct gsm_trans *trans, struct msgb *msg,
|
|
uint8_t sm_rp_mr, uint8_t *sm_rp_da, uint8_t sm_rp_da_len)
|
|
{
|
|
uint8_t bcd_buf[GSM48_MI_SIZE];
|
|
struct osmo_gsup_message gsup_msg;
|
|
size_t bcd_len;
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
|
|
|
|
LOG_TRANS(trans, LOGL_DEBUG, "TX GSUP MO-forwardSM-Req\n");
|
|
|
|
/* Assign SM-RP-MR to transaction state */
|
|
trans->sms.sm_rp_mr = sm_rp_mr;
|
|
|
|
/* Encode subscriber's MSISDN as LHV (with room for ToN/NPI header) */
|
|
bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf),
|
|
1, trans->vsub->msisdn);
|
|
if (bcd_len <= 0 || bcd_len > sizeof(bcd_buf)) {
|
|
LOG_TRANS(trans, LOGL_ERROR, "Failed to encode subscriber's MSISDN\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* NOTE: assuming default ToN/NPI values as we don't have this info */
|
|
bcd_buf[1] = 0x01 /* NPI: ISDN/Telephony Numbering (ITU-T Rec. E.164 / ITU-T Rec. E.163) */
|
|
| (0x01 << 4) /* ToN: International Number */
|
|
| (0x01 << 7); /* No Extension */
|
|
|
|
/* Initialize a new GSUP message */
|
|
gsup_sm_msg_init(&gsup_msg, OSMO_GSUP_MSGT_MO_FORWARD_SM_REQUEST,
|
|
trans->vsub->imsi, &sm_rp_mr);
|
|
|
|
/* According to 12.2.3, the MSISDN from VLR is inserted here.
|
|
* NOTE: redundant BCD length octet is not included. */
|
|
gsup_msg.sm_rp_oa_type = OSMO_GSUP_SMS_SM_RP_ODA_MSISDN;
|
|
gsup_msg.sm_rp_oa_len = bcd_len - 1;
|
|
gsup_msg.sm_rp_oa = bcd_buf + 1;
|
|
|
|
/* SM-RP-DA should (already) contain SMSC address */
|
|
gsup_msg.sm_rp_da_type = OSMO_GSUP_SMS_SM_RP_ODA_SMSC_ADDR;
|
|
gsup_msg.sm_rp_da_len = sm_rp_da_len;
|
|
gsup_msg.sm_rp_da = sm_rp_da;
|
|
|
|
/* SM-RP-UI (TPDU) is pointed by msgb->l4h */
|
|
gsup_msg.sm_rp_ui_len = msgb_l4len(msg);
|
|
gsup_msg.sm_rp_ui = (uint8_t *) msgb_sms(msg);
|
|
|
|
gsup_client_mux_tx_set_source(trans->net->gcm, &gsup_msg);
|
|
return gsup_client_mux_tx(trans->net->gcm, &gsup_msg);
|
|
}
|
|
|
|
int gsm411_gsup_mo_ready_for_sm_req(struct gsm_trans *trans, uint8_t sm_rp_mr)
|
|
{
|
|
struct osmo_gsup_message gsup_msg;
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
|
|
|
|
LOG_TRANS(trans, LOGL_DEBUG, "TX GSUP READY-FOR-SM Req\n");
|
|
|
|
/* Assign SM-RP-MR to transaction state */
|
|
trans->sms.sm_rp_mr = sm_rp_mr;
|
|
|
|
/* Initialize a new GSUP message */
|
|
gsup_sm_msg_init(&gsup_msg, OSMO_GSUP_MSGT_READY_FOR_SM_REQUEST,
|
|
trans->vsub->imsi, &sm_rp_mr);
|
|
|
|
/* Indicate SMMA as the Alert Reason */
|
|
gsup_msg.sm_alert_rsn = OSMO_GSUP_SMS_SM_ALERT_RSN_MEM_AVAIL;
|
|
|
|
gsup_client_mux_tx_set_source(trans->net->gcm, &gsup_msg);
|
|
return gsup_client_mux_tx(trans->net->gcm, &gsup_msg);
|
|
}
|
|
|
|
/* Triggers either RP-ACK or RP-ERROR on response from SMSC */
|
|
static int gsm411_gsup_mo_handler(struct gsm_network *net, struct vlr_subscr *vsub,
|
|
const struct osmo_gsup_message *gsup_msg)
|
|
{
|
|
struct gsm_trans *trans;
|
|
const char *msg_name;
|
|
bool msg_is_err;
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
|
|
|
|
/* Determine the message type and name */
|
|
msg_is_err = OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type);
|
|
switch (gsup_msg->message_type) {
|
|
case OSMO_GSUP_MSGT_MO_FORWARD_SM_ERROR:
|
|
case OSMO_GSUP_MSGT_MO_FORWARD_SM_RESULT:
|
|
msg_name = "MO-forwardSM";
|
|
break;
|
|
case OSMO_GSUP_MSGT_READY_FOR_SM_ERROR:
|
|
case OSMO_GSUP_MSGT_READY_FOR_SM_RESULT:
|
|
msg_name = "MO-ReadyForSM";
|
|
break;
|
|
default:
|
|
/* Shall not happen */
|
|
OSMO_ASSERT(0);
|
|
}
|
|
|
|
/* Verify GSUP message */
|
|
if (!gsup_msg->sm_rp_mr)
|
|
goto msg_error;
|
|
if (msg_is_err && !gsup_msg->sm_rp_cause)
|
|
goto msg_error;
|
|
|
|
/* Attempt to find DTAP-transaction */
|
|
trans = trans_find_by_sm_rp_mr(net, vsub, *(gsup_msg->sm_rp_mr));
|
|
if (!trans) {
|
|
LOGP(DLSMS, LOGL_NOTICE, "No transaction found for %s, "
|
|
"ignoring %s-%s message...\n", vlr_subscr_name(vsub),
|
|
msg_name, msg_is_err ? "Err" : "Res");
|
|
gsup_client_mux_tx_error_reply(net->gcm, gsup_msg, GMM_CAUSE_NO_PDP_ACTIVATED);
|
|
return -EIO;
|
|
}
|
|
|
|
LOG_TRANS(trans, LOGL_DEBUG, "RX %s-%s\n", msg_name, msg_is_err ? "Err" : "Res");
|
|
|
|
/* Send either RP-ERROR, or RP-ACK */
|
|
if (msg_is_err) {
|
|
/* TODO: handle optional SM-RP-UI payload (requires API change) */
|
|
gsm411_send_rp_error(trans, *(gsup_msg->sm_rp_mr),
|
|
*(gsup_msg->sm_rp_cause));
|
|
} else {
|
|
gsm411_send_rp_ack(trans, *(gsup_msg->sm_rp_mr));
|
|
}
|
|
|
|
return 0;
|
|
|
|
msg_error:
|
|
LOGP(DLSMS, LOGL_NOTICE, "RX malformed %s-%s\n", msg_name, msg_is_err ? "Err" : "Res");
|
|
gsup_client_mux_tx_error_reply(net->gcm, gsup_msg, GMM_CAUSE_INV_MAND_INFO);
|
|
return -EINVAL;
|
|
}
|
|
|
|
int gsm411_gsup_mt_fwd_sm_res(struct gsm_trans *trans, uint8_t sm_rp_mr)
|
|
{
|
|
struct osmo_gsup_message gsup_msg;
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
|
|
|
|
LOG_TRANS(trans, LOGL_DEBUG, "TX MT-forwardSM-Res\n");
|
|
|
|
/* Initialize a new GSUP message */
|
|
gsup_sm_msg_init(&gsup_msg, OSMO_GSUP_MSGT_MT_FORWARD_SM_RESULT,
|
|
trans->vsub->imsi, &sm_rp_mr);
|
|
|
|
/* Ensure routing through OsmoHLR to the MT-sending SMSC */
|
|
gsup_msg.destination_name = trans->sms.gsup_source_name;
|
|
gsup_msg.destination_name_len = trans->sms.gsup_source_name_len;
|
|
gsup_client_mux_tx_set_source(trans->net->gcm, &gsup_msg);
|
|
|
|
return gsup_client_mux_tx(trans->net->gcm, &gsup_msg);
|
|
}
|
|
|
|
int gsm411_gsup_mt_fwd_sm_err(struct gsm_trans *trans,
|
|
uint8_t sm_rp_mr, uint8_t cause)
|
|
{
|
|
struct osmo_gsup_message gsup_msg;
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub);
|
|
|
|
LOG_TRANS(trans, LOGL_DEBUG, "TX MT-forwardSM-Err\n");
|
|
|
|
/* Initialize a new GSUP message */
|
|
gsup_sm_msg_init(&gsup_msg, OSMO_GSUP_MSGT_MT_FORWARD_SM_ERROR,
|
|
trans->vsub->imsi, &sm_rp_mr);
|
|
|
|
/* Ensure routing through OsmoHLR to the MT-sending SMSC */
|
|
gsup_msg.destination_name = trans->sms.gsup_source_name;
|
|
gsup_msg.destination_name_len = trans->sms.gsup_source_name_len;
|
|
gsup_client_mux_tx_set_source(trans->net->gcm, &gsup_msg);
|
|
|
|
/* SM-RP-Cause value */
|
|
gsup_msg.sm_rp_cause = &cause;
|
|
|
|
/* TODO: include optional SM-RP-UI field if present */
|
|
return gsup_client_mux_tx(trans->net->gcm, &gsup_msg);
|
|
}
|
|
|
|
/* Handles MT SMS (and triggers Paging Request if required) */
|
|
static int gsm411_gsup_mt_handler(struct gsm_network *net, struct vlr_subscr *vsub,
|
|
const struct osmo_gsup_message *gsup_msg)
|
|
{
|
|
bool sm_rp_mmts_ind;
|
|
int rc;
|
|
|
|
/* Associate logging messages with this subscriber */
|
|
log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
|
|
|
|
LOGP(DLSMS, LOGL_DEBUG, "RX MT-forwardSM-Req\n");
|
|
|
|
/**
|
|
* Verify GSUP message
|
|
*
|
|
* FIXME: SM-RP-MR is not known yet (to be assigned by MSC)
|
|
* NOTE: SM-RP-DA is out of our interest
|
|
*/
|
|
if (!gsup_msg->sm_rp_mr)
|
|
goto msg_error;
|
|
if (!gsup_msg->sm_rp_ui)
|
|
goto msg_error;
|
|
|
|
/* SM-RP-OA shall contain SMSC address */
|
|
if (gsup_msg->sm_rp_oa_type != OSMO_GSUP_SMS_SM_RP_ODA_SMSC_ADDR)
|
|
goto msg_error;
|
|
|
|
/* MMS (More Messages to Send) IE is optional */
|
|
if (gsup_msg->sm_rp_mms)
|
|
sm_rp_mmts_ind = *gsup_msg->sm_rp_mms > 0;
|
|
else
|
|
sm_rp_mmts_ind = false;
|
|
|
|
/* Send RP-DATA */
|
|
rc = gsm411_send_rp_data(net, vsub,
|
|
gsup_msg->sm_rp_oa_len, gsup_msg->sm_rp_oa,
|
|
gsup_msg->sm_rp_ui_len, gsup_msg->sm_rp_ui,
|
|
sm_rp_mmts_ind, gsup_msg->source_name,
|
|
gsup_msg->source_name_len);
|
|
if (rc) {
|
|
LOGP(DLSMS, LOGL_NOTICE, "Failed to send MT SMS, "
|
|
"ignoring MT-forwardSM-Req message...\n");
|
|
gsup_client_mux_tx_error_reply(net->gcm, gsup_msg, GMM_CAUSE_NET_FAIL);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
|
|
msg_error:
|
|
LOGP(DLSMS, LOGL_NOTICE, "RX malformed MT-forwardSM-Req\n");
|
|
gsup_client_mux_tx_error_reply(net->gcm, gsup_msg, GMM_CAUSE_INV_MAND_INFO);
|
|
return -EINVAL;
|
|
}
|
|
|
|
int gsm411_gsup_rx(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message *gsup_msg)
|
|
{
|
|
struct gsm_network *net = (struct gsm_network *) data;
|
|
struct vlr_subscr *vsub;
|
|
int rc;
|
|
|
|
/* Make sure that 'SMS over GSUP' is expected */
|
|
if (!net->sms_over_gsup) {
|
|
LOGP(DLSMS, LOGL_NOTICE, "Unexpected MO/MT SMS over GSUP "
|
|
"(sms-over-gsup is not enabled), ignoring message...\n");
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_GPRS_NOTALLOWED);
|
|
return -EIO;
|
|
}
|
|
|
|
vsub = vlr_subscr_find_by_imsi(net->vlr, gsup_msg->imsi, __func__);
|
|
if (!vsub) {
|
|
LOGP(DLSMS, LOGL_ERROR, "Rx %s for unknown subscriber, rejecting\n",
|
|
osmo_gsup_message_type_name(gsup_msg->message_type));
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_IMSI_UNKNOWN);
|
|
return -GMM_CAUSE_IMSI_UNKNOWN;
|
|
}
|
|
|
|
switch (gsup_msg->message_type) {
|
|
/* GSM 04.11 code implementing MO SMS */
|
|
case OSMO_GSUP_MSGT_MO_FORWARD_SM_ERROR:
|
|
case OSMO_GSUP_MSGT_MO_FORWARD_SM_RESULT:
|
|
case OSMO_GSUP_MSGT_READY_FOR_SM_ERROR:
|
|
case OSMO_GSUP_MSGT_READY_FOR_SM_RESULT:
|
|
DEBUGP(DMSC, "Routed to GSM 04.11 MO handler\n");
|
|
rc = gsm411_gsup_mo_handler(net, vsub, gsup_msg);
|
|
break;
|
|
|
|
/* GSM 04.11 code implementing MT SMS */
|
|
case OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST:
|
|
DEBUGP(DMSC, "Routed to GSM 04.11 MT handler\n");
|
|
rc = gsm411_gsup_mt_handler(net, vsub, gsup_msg);
|
|
break;
|
|
|
|
default:
|
|
LOGP(DMM, LOGL_ERROR, "No handler found for %s, dropping message...\n",
|
|
osmo_gsup_message_type_name(gsup_msg->message_type));
|
|
gsup_client_mux_tx_error_reply(gcm, gsup_msg, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL);
|
|
rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
|
|
}
|
|
|
|
vlr_subscr_put(vsub, __func__);
|
|
return rc;
|
|
}
|