| /* |
| * (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); |
| |
| 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; |
| |
| 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; |
| } |