2019-11-14 16:49:08 +00:00
|
|
|
/* SMS queue to continuously attempt to deliver SMS */
|
2010-12-24 12:48:27 +00:00
|
|
|
/*
|
|
|
|
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
2011-01-01 14:25:50 +00:00
|
|
|
* 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
|
2010-12-24 12:48:27 +00:00
|
|
|
* (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
|
2011-01-01 14:25:50 +00:00
|
|
|
* GNU Affero General Public License for more details.
|
2010-12-24 12:48:27 +00:00
|
|
|
*
|
2011-01-01 14:25:50 +00:00
|
|
|
* 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/>.
|
2010-12-24 12:48:27 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The difficulty of such a queue is to send a lot of SMS without
|
|
|
|
* overloading the paging subsystem and the database and other users
|
|
|
|
* of the MSC. To make the best use we would need to know the number
|
|
|
|
* of pending paging requests, then throttle the number of SMS we
|
|
|
|
* want to send and such.
|
|
|
|
* We will start with a very simple SMS Queue and then try to speed
|
|
|
|
* things up by collecting data from other parts of the system.
|
|
|
|
*/
|
|
|
|
|
2016-06-19 16:06:02 +00:00
|
|
|
#include <limits.h>
|
|
|
|
|
2017-09-04 13:04:35 +00:00
|
|
|
#include <osmocom/msc/sms_queue.h>
|
|
|
|
#include <osmocom/msc/db.h>
|
|
|
|
#include <osmocom/msc/debug.h>
|
|
|
|
#include <osmocom/msc/gsm_data.h>
|
|
|
|
#include <osmocom/msc/gsm_04_11.h>
|
|
|
|
#include <osmocom/msc/gsm_subscriber.h>
|
|
|
|
#include <osmocom/msc/signal.h>
|
|
|
|
#include <osmocom/msc/vlr.h>
|
2010-12-24 12:48:27 +00:00
|
|
|
|
2011-03-22 15:47:59 +00:00
|
|
|
#include <osmocom/core/talloc.h>
|
2022-05-15 11:03:57 +00:00
|
|
|
#include <osmocom/core/utils.h>
|
|
|
|
#include <osmocom/core/rate_ctr.h>
|
|
|
|
#include <osmocom/core/stat_item.h>
|
2010-12-24 12:48:27 +00:00
|
|
|
|
2010-12-25 13:08:00 +00:00
|
|
|
#include <osmocom/vty/vty.h>
|
|
|
|
|
2022-05-15 11:03:57 +00:00
|
|
|
enum smsq_stat_item_idx {
|
|
|
|
SMSQ_STAT_SMS_RAM_PENDING,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct osmo_stat_item_desc smsq_stat_item_desc[] = {
|
|
|
|
[SMSQ_STAT_SMS_RAM_PENDING] = { "ram:pending",
|
|
|
|
"Number of SMSs in the in-RAM pending delivery queue" },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct osmo_stat_item_group_desc smsq_statg_desc = {
|
|
|
|
"sms_queue",
|
|
|
|
"SMS queue",
|
|
|
|
OSMO_STATS_CLASS_GLOBAL,
|
|
|
|
ARRAY_SIZE(smsq_stat_item_desc),
|
|
|
|
smsq_stat_item_desc,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum smsq_rate_ctr_idx {
|
|
|
|
SMSQ_CTR_SMS_DELIVERY_ATTEMPTS,
|
|
|
|
SMSQ_CTR_SMS_DELIVERY_ACK,
|
|
|
|
SMSQ_CTR_SMS_DELIVERY_ERR,
|
|
|
|
SMSQ_CTR_SMS_DELIVERY_NOMEM,
|
|
|
|
SMSQ_CTR_SMS_DELIVERY_TIMEOUT,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rate_ctr_desc smsq_ctr_desc[] = {
|
|
|
|
[SMSQ_CTR_SMS_DELIVERY_ATTEMPTS] = { "delivery:attempts",
|
|
|
|
"Attempted MT SMS deliveries to subscriber" },
|
|
|
|
[SMSQ_CTR_SMS_DELIVERY_ACK] = { "deliver:ack",
|
|
|
|
"Successful MT SMS delivery to subscriber" },
|
|
|
|
[SMSQ_CTR_SMS_DELIVERY_ERR] = { "deliver:error",
|
|
|
|
"Erroneous MT SMS delivery" },
|
|
|
|
[SMSQ_CTR_SMS_DELIVERY_NOMEM] = { "deliver:no_memory",
|
|
|
|
"Failed MT SMS delivery due to no memory on MS" },
|
|
|
|
[SMSQ_CTR_SMS_DELIVERY_TIMEOUT] = { "deliver:paging_timeout",
|
|
|
|
"Failed MT SMS delivery due to paging timeout (MS gone?)" },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct rate_ctr_group_desc smsq_ctrg_desc = {
|
|
|
|
"sms_queue",
|
|
|
|
"SMS queue",
|
|
|
|
OSMO_STATS_CLASS_GLOBAL,
|
|
|
|
ARRAY_SIZE(smsq_ctr_desc),
|
|
|
|
smsq_ctr_desc,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define smsq_rate_ctr_inc(smsq, idx) \
|
|
|
|
rate_ctr_inc(rate_ctr_group_get_ctr((smsq)->ctrg, idx))
|
|
|
|
#define smsq_rate_ctr_add(smsq, idx, val) \
|
|
|
|
rate_ctr_add(rate_ctr_group_get_ctr((smsq)->ctrg, idx), val)
|
|
|
|
|
|
|
|
#define smsq_stat_item_inc(smsq, idx) \
|
|
|
|
osmo_stat_item_inc(osmo_stat_item_group_get_item((smsq)->statg, idx), 1)
|
|
|
|
#define smsq_stat_item_dec(smsq, idx) \
|
|
|
|
osmo_stat_item_dec(osmo_stat_item_group_get_item((smsq)->statg, idx), 1)
|
|
|
|
#define smsq_stat_item_set(smsq, idx, val) \
|
|
|
|
osmo_stat_item_set(osmo_stat_item_group_get_item((smsq)->statg, idx), val)
|
|
|
|
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* One in-RAM record of a "pending SMS". This is not the SMS itself, but merely
|
|
|
|
* a pointer to the database record. It holds a reference on the vlr_subscriber
|
|
|
|
* and some counters. While this object exists in RAM, we are regularly attempting
|
|
|
|
* to deliver the related SMS. */
|
2010-12-24 20:39:55 +00:00
|
|
|
struct gsm_sms_pending {
|
2022-05-14 07:54:15 +00:00
|
|
|
struct llist_head entry; /* gsm_sms_queue.pending_sms */
|
2010-12-24 20:39:55 +00:00
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
struct vlr_subscr *vsub; /* destination subscriber for this SMS */
|
|
|
|
struct msc_a *msc_a; /* MSC_A associated with this SMS */
|
|
|
|
unsigned long long sms_id; /* unique ID (in SQL database) of this SMS */
|
|
|
|
int failed_attempts; /* count of failed deliver attempts so far */
|
|
|
|
int resend; /* should we try re-sending it (now) ? */
|
2010-12-24 20:39:55 +00:00
|
|
|
};
|
2010-12-24 12:48:27 +00:00
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* (global) state of the SMS queue. */
|
2010-12-24 12:48:27 +00:00
|
|
|
struct gsm_sms_queue {
|
2022-05-14 07:54:15 +00:00
|
|
|
struct osmo_timer_list resend_pending; /* timer triggering sms_resend_pending() */
|
|
|
|
struct osmo_timer_list push_queue; /* timer triggering sms_submit_pending() */
|
2010-12-24 12:48:27 +00:00
|
|
|
struct gsm_network *network;
|
2022-05-14 07:54:15 +00:00
|
|
|
struct llist_head pending_sms; /* list of gsm_sms_pending */
|
2022-05-17 11:31:14 +00:00
|
|
|
struct sms_queue_config *cfg;
|
|
|
|
int pending; /* current number of gsm_sms_pending in RAM */
|
2016-06-19 16:06:02 +00:00
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* last MSISDN for which we read SMS from the database and created gsm_sms_pending records */
|
2019-05-25 12:27:17 +00:00
|
|
|
char last_msisdn[GSM23003_MSISDN_MAX_DIGITS+1];
|
2022-05-15 11:03:57 +00:00
|
|
|
|
|
|
|
/* statistics / counters */
|
|
|
|
struct osmo_stat_item_group *statg;
|
|
|
|
struct rate_ctr_group *ctrg;
|
2010-12-24 12:48:27 +00:00
|
|
|
};
|
|
|
|
|
2022-05-15 11:03:57 +00:00
|
|
|
/* private wrapper function to make sure we count all SMS delivery attempts */
|
|
|
|
static void _gsm411_send_sms(struct gsm_network *net, struct vlr_subscr *vsub, struct gsm_sms *sms)
|
|
|
|
{
|
|
|
|
smsq_rate_ctr_inc(net->sms_queue, SMSQ_CTR_SMS_DELIVERY_ATTEMPTS);
|
|
|
|
gsm411_send_sms(net, vsub, sms);
|
|
|
|
}
|
|
|
|
|
2010-12-24 20:39:55 +00:00
|
|
|
static int sms_subscr_cb(unsigned int, unsigned int, void *, void *);
|
|
|
|
static int sms_sms_cb(unsigned int, unsigned int, void *, void *);
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* look-up a 'gsm_sms_pending' for the given sms_id; return NULL if none */
|
2010-12-24 20:39:55 +00:00
|
|
|
static struct gsm_sms_pending *sms_find_pending(struct gsm_sms_queue *smsq,
|
2018-01-22 16:05:37 +00:00
|
|
|
unsigned long long sms_id)
|
2010-12-24 20:39:55 +00:00
|
|
|
{
|
|
|
|
struct gsm_sms_pending *pending;
|
|
|
|
|
|
|
|
llist_for_each_entry(pending, &smsq->pending_sms, entry) {
|
2018-01-22 16:05:37 +00:00
|
|
|
if (pending->sms_id == sms_id)
|
2010-12-24 20:39:55 +00:00
|
|
|
return pending;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* do we currently have a gsm_sms_pending object for the given SMS id? */
|
2018-01-22 16:05:37 +00:00
|
|
|
int sms_queue_sms_is_pending(struct gsm_sms_queue *smsq, unsigned long long sms_id)
|
2010-12-24 20:39:55 +00:00
|
|
|
{
|
2018-01-22 16:05:37 +00:00
|
|
|
return sms_find_pending(smsq, sms_id) != NULL;
|
2010-12-24 20:39:55 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* find the first pending SMS (in RAM) for the given subscriber */
|
2011-07-24 22:13:06 +00:00
|
|
|
static struct gsm_sms_pending *sms_subscriber_find_pending(
|
|
|
|
struct gsm_sms_queue *smsq,
|
2016-06-19 16:06:02 +00:00
|
|
|
struct vlr_subscr *vsub)
|
2010-12-24 20:39:55 +00:00
|
|
|
{
|
|
|
|
struct gsm_sms_pending *pending;
|
|
|
|
|
|
|
|
llist_for_each_entry(pending, &smsq->pending_sms, entry) {
|
2016-06-19 16:06:02 +00:00
|
|
|
if (pending->vsub == vsub)
|
2011-07-24 22:13:06 +00:00
|
|
|
return pending;
|
2010-12-24 20:39:55 +00:00
|
|
|
}
|
|
|
|
|
2011-07-24 22:13:06 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* do we have any pending SMS (in RAM) for the given subscriber? */
|
2011-07-24 22:13:06 +00:00
|
|
|
static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq,
|
2016-06-19 16:06:02 +00:00
|
|
|
struct vlr_subscr *vsub)
|
2011-07-24 22:13:06 +00:00
|
|
|
{
|
2016-06-19 16:06:02 +00:00
|
|
|
return sms_subscriber_find_pending(smsq, vsub) != NULL;
|
2010-12-24 20:39:55 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* allocate a new gsm_sms_pending record and fill it with information from 'sms' */
|
2010-12-24 20:39:55 +00:00
|
|
|
static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq,
|
|
|
|
struct gsm_sms *sms)
|
|
|
|
{
|
|
|
|
struct gsm_sms_pending *pending;
|
|
|
|
|
|
|
|
pending = talloc_zero(smsq, struct gsm_sms_pending);
|
|
|
|
if (!pending)
|
|
|
|
return NULL;
|
|
|
|
|
2019-02-19 01:36:35 +00:00
|
|
|
vlr_subscr_get(sms->receiver, VSUB_USE_SMS_PENDING);
|
|
|
|
pending->vsub = sms->receiver;
|
2010-12-24 20:39:55 +00:00
|
|
|
pending->sms_id = sms->id;
|
2022-05-15 12:58:50 +00:00
|
|
|
llist_add_tail(&pending->entry, &smsq->pending_sms);
|
2010-12-24 20:39:55 +00:00
|
|
|
|
2022-05-15 12:55:56 +00:00
|
|
|
smsq->pending += 1;
|
|
|
|
smsq_stat_item_inc(smsq, SMSQ_STAT_SMS_RAM_PENDING);
|
2022-05-15 12:58:50 +00:00
|
|
|
|
|
|
|
return pending;
|
2022-05-15 12:55:56 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* release a gsm_sms_pending object */
|
2022-05-15 12:55:56 +00:00
|
|
|
static void sms_pending_free(struct gsm_sms_queue *smsq, struct gsm_sms_pending *pending)
|
2010-12-24 20:39:55 +00:00
|
|
|
{
|
2022-05-15 12:55:56 +00:00
|
|
|
smsq->pending -= 1;
|
|
|
|
smsq_stat_item_dec(smsq, SMSQ_STAT_SMS_RAM_PENDING);
|
2019-02-19 01:36:35 +00:00
|
|
|
vlr_subscr_put(pending->vsub, VSUB_USE_SMS_PENDING);
|
2010-12-24 20:39:55 +00:00
|
|
|
llist_del(&pending->entry);
|
|
|
|
talloc_free(pending);
|
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* this sets the 'resend' flag of the gsm_sms_pending and schedules
|
|
|
|
* the timer for re-sending */
|
2010-12-24 20:39:55 +00:00
|
|
|
static void sms_pending_resend(struct gsm_sms_pending *pending)
|
|
|
|
{
|
2016-06-19 16:06:02 +00:00
|
|
|
struct gsm_network *net = pending->vsub->vlr->user_ctx;
|
2010-12-24 20:39:55 +00:00
|
|
|
struct gsm_sms_queue *smsq;
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG,
|
2010-12-24 20:39:55 +00:00
|
|
|
"Scheduling resend of SMS %llu.\n", pending->sms_id);
|
|
|
|
|
|
|
|
pending->resend = 1;
|
|
|
|
|
2016-06-19 16:06:02 +00:00
|
|
|
smsq = net->sms_queue;
|
2011-05-06 10:11:06 +00:00
|
|
|
if (osmo_timer_pending(&smsq->resend_pending))
|
2010-12-24 20:39:55 +00:00
|
|
|
return;
|
|
|
|
|
2011-05-06 10:11:06 +00:00
|
|
|
osmo_timer_schedule(&smsq->resend_pending, 1, 0);
|
2010-12-24 20:39:55 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* call-back when a pending SMS has failed; try another re-send if number of
|
|
|
|
* attempts is < smsq->max_fail */
|
2010-12-24 20:39:55 +00:00
|
|
|
static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error)
|
|
|
|
{
|
2016-06-19 16:06:02 +00:00
|
|
|
struct gsm_network *net = pending->vsub->vlr->user_ctx;
|
2010-12-24 20:39:55 +00:00
|
|
|
struct gsm_sms_queue *smsq;
|
|
|
|
|
2019-03-06 15:00:41 +00:00
|
|
|
pending->failed_attempts++;
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n",
|
2010-12-24 20:39:55 +00:00
|
|
|
pending->sms_id, pending->failed_attempts);
|
|
|
|
|
2016-06-19 16:06:02 +00:00
|
|
|
smsq = net->sms_queue;
|
2022-05-17 11:31:14 +00:00
|
|
|
if (pending->failed_attempts < smsq->cfg->max_fail)
|
2010-12-24 20:39:55 +00:00
|
|
|
return sms_pending_resend(pending);
|
|
|
|
|
2022-05-15 12:55:56 +00:00
|
|
|
sms_pending_free(smsq, pending);
|
2010-12-24 20:39:55 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* Resend all SMS that are scheduled for a resend. This is done to
|
|
|
|
* avoid an immediate failure. This iterates over all the (in RAM)
|
|
|
|
* pending_sms records, checks for resend == true, reads them from the
|
2022-05-15 11:03:57 +00:00
|
|
|
* DB and attempts to send them via _gsm411_send_sms() */
|
2010-12-24 20:39:55 +00:00
|
|
|
static void sms_resend_pending(void *_data)
|
|
|
|
{
|
|
|
|
struct gsm_sms_pending *pending, *tmp;
|
|
|
|
struct gsm_sms_queue *smsq = _data;
|
|
|
|
|
|
|
|
llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
|
|
|
|
struct gsm_sms *sms;
|
|
|
|
if (!pending->resend)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sms = db_sms_get(smsq->network, pending->sms_id);
|
|
|
|
|
|
|
|
/* the sms is gone? Move to the next */
|
|
|
|
if (!sms) {
|
2022-05-15 12:55:56 +00:00
|
|
|
sms_pending_free(smsq, pending);
|
2010-12-24 20:39:55 +00:00
|
|
|
sms_queue_trigger(smsq);
|
|
|
|
} else {
|
|
|
|
pending->resend = 0;
|
2022-05-15 11:03:57 +00:00
|
|
|
_gsm411_send_sms(smsq->network, sms->receiver, sms);
|
2010-12-24 20:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-19 16:06:02 +00:00
|
|
|
/* Find the next pending SMS by cycling through the recipients. We could also
|
|
|
|
* cycle through the pending SMS, but that might cause us to keep trying to
|
|
|
|
* send SMS to the same few subscribers repeatedly while not servicing other
|
|
|
|
* subscribers for a long time. By walking the list of recipient MSISDNs, we
|
|
|
|
* ensure that all subscribers get their fair time to receive SMS. */
|
|
|
|
struct gsm_sms *smsq_take_next_sms(struct gsm_network *net,
|
|
|
|
char *last_msisdn,
|
|
|
|
size_t last_msisdn_buflen)
|
2010-12-25 16:45:23 +00:00
|
|
|
{
|
|
|
|
struct gsm_sms *sms;
|
2016-06-19 16:06:02 +00:00
|
|
|
int wrapped = 0;
|
|
|
|
int sanity = 100;
|
|
|
|
char started_with_msisdn[last_msisdn_buflen];
|
|
|
|
|
2018-02-05 11:57:06 +00:00
|
|
|
OSMO_STRLCPY_ARRAY(started_with_msisdn, last_msisdn);
|
2016-06-19 16:06:02 +00:00
|
|
|
|
|
|
|
while (wrapped < 2 && (--sanity)) {
|
|
|
|
/* If we wrapped around and passed the first msisdn, we're
|
|
|
|
* through the entire SMS DB; end it. */
|
|
|
|
if (wrapped && strcmp(last_msisdn, started_with_msisdn) >= 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
sms = db_sms_get_next_unsent_rr_msisdn(net, last_msisdn, 9);
|
|
|
|
if (!sms) {
|
|
|
|
last_msisdn[0] = '\0';
|
2018-02-06 18:31:08 +00:00
|
|
|
wrapped++;
|
2016-06-19 16:06:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Whatever happens, next time around service another recipient
|
|
|
|
*/
|
|
|
|
osmo_strlcpy(last_msisdn, sms->dst.addr, last_msisdn_buflen);
|
|
|
|
|
|
|
|
/* Is the subscriber attached? If not, go to next SMS */
|
libmsc/sms_queue.c: fix memleak in smsq_take_next_sms()
A memleak has been noticed after executing some of TTCN-3 test
cases. For example, the following ones:
- MSC_Tests.TC_lu_and_mo_sms,
- MSC_Tests.TC_lu_and_mt_sms.
The key point is that MSC_Tests.TC_lu_and_mo_sms basically sends
a MO SMS to a non-attached subscriber with MSISDN 12345, so this
message is getting stored in the SMSC's database.
As soon as the SMSC's queue is triggered, sms_submit_pending() would
retrieve pending messages from the database by calling function
smsq_take_next_sms() in loop and attempt to deliver them.
This function in it's turn checks whether the subscriber is attached
or not. If not, the allocated 'gsm_sms' structure would not be
free()ed! Therefore, every time smsq_take_next_sms() is called,
one 'gsm_sms' structure for an unattached subscriber is leaked.
Furthermore, there is a unit test called 'sms_queue_test', that
actually does cover smsq_take_next_sms() and was designed to
catch some potential memory leaks, but...
In order to avoid emulating the low-level SQLite API, the unit
test by design overwrites some functions of libmsc, including
db_sms_get_next_unsent_rr_msisdn(), that is being called by
smsq_take_next_sms().
The problem is that the original function in libmsc does
allocate a 'gsm_sms' structure on heap (using talloc), while
the overwriting function did this statically, returning a
pointer to stack. This critical difference made it impossible
to spot the memleak in smsq_take_next_sms() during the
unit test execution.
Let's refactor 'sms_queue_test' to use dynamic memory allocation,
and finally fix the evil memleak in smsq_take_next_sms().
Change-Id: Iad5e4d84d8d410ea43d5907e9ddf6e5fdb55bc7a
Closes: OS#3860
2019-03-28 14:25:14 +00:00
|
|
|
if (!sms->receiver || !sms->receiver->lu_complete) {
|
|
|
|
LOGP(DLSMS, LOGL_DEBUG,
|
2019-04-09 10:44:35 +00:00
|
|
|
"Subscriber %s%s is not attached, skipping SMS %llu\n",
|
|
|
|
sms->receiver ? "" : "MSISDN-",
|
|
|
|
sms->receiver ? vlr_subscr_msisdn_or_name(sms->receiver)
|
|
|
|
: sms->dst.addr, sms->id);
|
libmsc/sms_queue.c: fix memleak in smsq_take_next_sms()
A memleak has been noticed after executing some of TTCN-3 test
cases. For example, the following ones:
- MSC_Tests.TC_lu_and_mo_sms,
- MSC_Tests.TC_lu_and_mt_sms.
The key point is that MSC_Tests.TC_lu_and_mo_sms basically sends
a MO SMS to a non-attached subscriber with MSISDN 12345, so this
message is getting stored in the SMSC's database.
As soon as the SMSC's queue is triggered, sms_submit_pending() would
retrieve pending messages from the database by calling function
smsq_take_next_sms() in loop and attempt to deliver them.
This function in it's turn checks whether the subscriber is attached
or not. If not, the allocated 'gsm_sms' structure would not be
free()ed! Therefore, every time smsq_take_next_sms() is called,
one 'gsm_sms' structure for an unattached subscriber is leaked.
Furthermore, there is a unit test called 'sms_queue_test', that
actually does cover smsq_take_next_sms() and was designed to
catch some potential memory leaks, but...
In order to avoid emulating the low-level SQLite API, the unit
test by design overwrites some functions of libmsc, including
db_sms_get_next_unsent_rr_msisdn(), that is being called by
smsq_take_next_sms().
The problem is that the original function in libmsc does
allocate a 'gsm_sms' structure on heap (using talloc), while
the overwriting function did this statically, returning a
pointer to stack. This critical difference made it impossible
to spot the memleak in smsq_take_next_sms() during the
unit test execution.
Let's refactor 'sms_queue_test' to use dynamic memory allocation,
and finally fix the evil memleak in smsq_take_next_sms().
Change-Id: Iad5e4d84d8d410ea43d5907e9ddf6e5fdb55bc7a
Closes: OS#3860
2019-03-28 14:25:14 +00:00
|
|
|
sms_free(sms);
|
2016-06-19 16:06:02 +00:00
|
|
|
continue;
|
libmsc/sms_queue.c: fix memleak in smsq_take_next_sms()
A memleak has been noticed after executing some of TTCN-3 test
cases. For example, the following ones:
- MSC_Tests.TC_lu_and_mo_sms,
- MSC_Tests.TC_lu_and_mt_sms.
The key point is that MSC_Tests.TC_lu_and_mo_sms basically sends
a MO SMS to a non-attached subscriber with MSISDN 12345, so this
message is getting stored in the SMSC's database.
As soon as the SMSC's queue is triggered, sms_submit_pending() would
retrieve pending messages from the database by calling function
smsq_take_next_sms() in loop and attempt to deliver them.
This function in it's turn checks whether the subscriber is attached
or not. If not, the allocated 'gsm_sms' structure would not be
free()ed! Therefore, every time smsq_take_next_sms() is called,
one 'gsm_sms' structure for an unattached subscriber is leaked.
Furthermore, there is a unit test called 'sms_queue_test', that
actually does cover smsq_take_next_sms() and was designed to
catch some potential memory leaks, but...
In order to avoid emulating the low-level SQLite API, the unit
test by design overwrites some functions of libmsc, including
db_sms_get_next_unsent_rr_msisdn(), that is being called by
smsq_take_next_sms().
The problem is that the original function in libmsc does
allocate a 'gsm_sms' structure on heap (using talloc), while
the overwriting function did this statically, returning a
pointer to stack. This critical difference made it impossible
to spot the memleak in smsq_take_next_sms() during the
unit test execution.
Let's refactor 'sms_queue_test' to use dynamic memory allocation,
and finally fix the evil memleak in smsq_take_next_sms().
Change-Id: Iad5e4d84d8d410ea43d5907e9ddf6e5fdb55bc7a
Closes: OS#3860
2019-03-28 14:25:14 +00:00
|
|
|
}
|
2010-12-25 16:45:23 +00:00
|
|
|
|
|
|
|
return sms;
|
|
|
|
}
|
|
|
|
|
2016-06-19 16:06:02 +00:00
|
|
|
DEBUGP(DLSMS, "SMS queue: no SMS to be sent\n");
|
|
|
|
return NULL;
|
2010-12-25 16:45:23 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* read up to 'max_pending' pending SMS from the database and add them to the in-memory
|
|
|
|
* sms_queue; trigger the first delivery attempt. 'submit' in this context means
|
|
|
|
* "read from the database and add to the in-memory gsm_sms_queue" and is not to be
|
|
|
|
* confused with the SMS SUBMIT operation a MS performs when sending a MO-SMS. */
|
2010-12-24 20:39:55 +00:00
|
|
|
static void sms_submit_pending(void *_data)
|
|
|
|
{
|
|
|
|
struct gsm_sms_queue *smsq = _data;
|
2022-05-17 11:31:14 +00:00
|
|
|
int attempts = smsq->cfg->max_pending - smsq->pending;
|
2010-12-25 18:28:44 +00:00
|
|
|
int initialized = 0;
|
|
|
|
unsigned long long first_sub = 0;
|
2010-12-25 18:32:12 +00:00
|
|
|
int attempted = 0, rounds = 0;
|
2010-12-24 20:39:55 +00:00
|
|
|
|
2019-06-20 08:45:35 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG, "Attempting to send up to %d SMS\n", attempts);
|
2010-12-24 20:39:55 +00:00
|
|
|
|
2010-12-25 18:28:44 +00:00
|
|
|
do {
|
2010-12-24 20:39:55 +00:00
|
|
|
struct gsm_sms_pending *pending;
|
|
|
|
struct gsm_sms *sms;
|
|
|
|
|
|
|
|
|
2016-06-19 16:06:02 +00:00
|
|
|
sms = smsq_take_next_sms(smsq->network, smsq->last_msisdn,
|
|
|
|
sizeof(smsq->last_msisdn));
|
2016-05-09 19:48:53 +00:00
|
|
|
if (!sms) {
|
|
|
|
LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (%d attempted)\n",
|
|
|
|
attempted);
|
2010-12-24 20:39:55 +00:00
|
|
|
break;
|
2016-05-09 19:48:53 +00:00
|
|
|
}
|
2010-12-24 20:39:55 +00:00
|
|
|
|
2010-12-25 18:32:12 +00:00
|
|
|
rounds += 1;
|
2019-11-18 06:15:56 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG, "Checking whether to send SMS %llu\n", sms->id);
|
2010-12-25 18:32:12 +00:00
|
|
|
|
2010-12-25 18:28:44 +00:00
|
|
|
/*
|
|
|
|
* This code needs to detect a loop. It assumes that no SMS
|
|
|
|
* will vanish during the time this is executed. We will remember
|
|
|
|
* the id of the first GSM subscriber we see and then will
|
|
|
|
* compare this. The Database code should make sure that we will
|
|
|
|
* see all other subscribers first before seeing this one again.
|
|
|
|
*
|
|
|
|
* It is always scary to have an infinite loop like this.
|
|
|
|
*/
|
|
|
|
if (!initialized) {
|
|
|
|
first_sub = sms->receiver->id;
|
|
|
|
initialized = 1;
|
|
|
|
} else if (first_sub == sms->receiver->id) {
|
2016-05-09 19:48:53 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (loop) (%d attempted)\n",
|
|
|
|
attempted);
|
2010-12-25 18:28:44 +00:00
|
|
|
sms_free(sms);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-12-24 20:39:55 +00:00
|
|
|
/* no need to send a pending sms */
|
2018-01-22 16:05:37 +00:00
|
|
|
if (sms_queue_sms_is_pending(smsq, sms->id)) {
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG,
|
2010-12-27 19:12:25 +00:00
|
|
|
"SMSqueue with pending sms: %llu. Skipping\n", sms->id);
|
2010-12-24 20:39:55 +00:00
|
|
|
sms_free(sms);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no need to send a SMS with the same receiver */
|
|
|
|
if (sms_subscriber_is_pending(smsq, sms->receiver)) {
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG,
|
2010-12-24 20:39:55 +00:00
|
|
|
"SMSqueue with pending sub: %llu. Skipping\n", sms->receiver->id);
|
|
|
|
sms_free(sms);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* allocate a new gsm_sms_pending object in RAM */
|
2010-12-24 20:39:55 +00:00
|
|
|
pending = sms_pending_from(smsq, sms);
|
|
|
|
if (!pending) {
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_ERROR,
|
2010-12-24 20:39:55 +00:00
|
|
|
"Failed to create pending SMS entry.\n");
|
|
|
|
sms_free(sms);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-12-25 18:28:44 +00:00
|
|
|
attempted += 1;
|
2022-05-15 11:03:57 +00:00
|
|
|
_gsm411_send_sms(smsq->network, sms->receiver, sms);
|
2010-12-25 18:32:12 +00:00
|
|
|
} while (attempted < attempts && rounds < 1000);
|
2010-12-25 18:28:44 +00:00
|
|
|
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG, "SMSqueue added %d messages in %d rounds\n", attempted, rounds);
|
2010-12-24 20:39:55 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* obtain the next pending SMS for given subscriber from database,
|
|
|
|
* create gsm_sms_pending object and attempt first delivery. If there
|
|
|
|
* are no SMS pending for the given subscriber, call sms_submit_pending()
|
|
|
|
* to read more SMS (for any subscriber) into the in-RAM pending queue */
|
2016-06-19 16:06:02 +00:00
|
|
|
static void sms_send_next(struct vlr_subscr *vsub)
|
2014-02-24 15:13:04 +00:00
|
|
|
{
|
2016-06-19 16:06:02 +00:00
|
|
|
struct gsm_network *net = vsub->vlr->user_ctx;
|
|
|
|
struct gsm_sms_queue *smsq = net->sms_queue;
|
2014-02-24 15:13:04 +00:00
|
|
|
struct gsm_sms_pending *pending;
|
|
|
|
struct gsm_sms *sms;
|
|
|
|
|
|
|
|
/* the subscriber should not be in the queue */
|
2016-06-19 16:06:02 +00:00
|
|
|
OSMO_ASSERT(!sms_subscriber_is_pending(smsq, vsub));
|
2014-02-24 15:13:04 +00:00
|
|
|
|
|
|
|
/* check for more messages for this subscriber */
|
2022-05-14 13:35:49 +00:00
|
|
|
sms = db_sms_get_unsent_for_subscr(vsub, INT_MAX);
|
2014-02-24 15:13:04 +00:00
|
|
|
if (!sms)
|
|
|
|
goto no_pending_sms;
|
|
|
|
|
2016-06-19 16:06:02 +00:00
|
|
|
/* The sms should not be scheduled right now */
|
2018-01-22 16:05:37 +00:00
|
|
|
OSMO_ASSERT(!sms_queue_sms_is_pending(smsq, sms->id));
|
2014-02-24 15:13:04 +00:00
|
|
|
|
|
|
|
/* Remember that we deliver this SMS and send it */
|
|
|
|
pending = sms_pending_from(smsq, sms);
|
|
|
|
if (!pending) {
|
|
|
|
LOGP(DLSMS, LOGL_ERROR,
|
|
|
|
"Failed to create pending SMS entry.\n");
|
|
|
|
sms_free(sms);
|
|
|
|
goto no_pending_sms;
|
|
|
|
}
|
|
|
|
|
2022-05-15 11:03:57 +00:00
|
|
|
_gsm411_send_sms(smsq->network, sms->receiver, sms);
|
2014-02-24 15:13:04 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
no_pending_sms:
|
|
|
|
/* Try to send the SMS to avoid the queue being stuck */
|
2016-06-19 16:06:02 +00:00
|
|
|
sms_submit_pending(net->sms_queue);
|
2014-02-24 15:13:04 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* Trigger a call to sms_submit_pending() in one second */
|
2010-12-24 20:39:55 +00:00
|
|
|
int sms_queue_trigger(struct gsm_sms_queue *smsq)
|
|
|
|
{
|
2016-05-09 19:48:53 +00:00
|
|
|
LOGP(DLSMS, LOGL_DEBUG, "Triggering SMS queue\n");
|
2011-05-06 10:11:06 +00:00
|
|
|
if (osmo_timer_pending(&smsq->push_queue))
|
2010-12-24 20:39:55 +00:00
|
|
|
return 0;
|
|
|
|
|
2011-05-06 10:11:06 +00:00
|
|
|
osmo_timer_schedule(&smsq->push_queue, 1, 0);
|
2010-12-24 20:39:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2010-12-24 12:48:27 +00:00
|
|
|
|
2022-05-17 11:31:14 +00:00
|
|
|
/* allocate + initialize SMS queue configuration with some default values */
|
|
|
|
struct sms_queue_config *sms_queue_cfg_alloc(void *ctx)
|
|
|
|
{
|
|
|
|
struct sms_queue_config *sqcfg = talloc_zero(ctx, struct sms_queue_config);
|
|
|
|
OSMO_ASSERT(sqcfg);
|
|
|
|
|
|
|
|
sqcfg->max_pending = 20;
|
|
|
|
sqcfg->max_fail = 1;
|
2022-05-17 12:39:51 +00:00
|
|
|
sqcfg->delete_delivered = true;
|
|
|
|
sqcfg->delete_expired = true;
|
2022-05-17 16:56:55 +00:00
|
|
|
sqcfg->default_validity_mins = 7 * 24 * 60; /* 7 days */
|
2022-05-17 17:01:43 +00:00
|
|
|
sqcfg->minimum_validity_mins = 1;
|
2022-05-17 11:31:14 +00:00
|
|
|
sqcfg->db_file_path = talloc_strdup(ctx, SMS_DEFAULT_DB_FILE_PATH);
|
|
|
|
|
|
|
|
return sqcfg;
|
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* initialize the sms_queue subsystem and read the first batch of SMS from
|
|
|
|
* the database for delivery */
|
2022-05-17 11:31:14 +00:00
|
|
|
int sms_queue_start(struct gsm_network *network)
|
2010-12-24 12:48:27 +00:00
|
|
|
{
|
|
|
|
struct gsm_sms_queue *sms = talloc_zero(network, struct gsm_sms_queue);
|
|
|
|
if (!sms) {
|
|
|
|
LOGP(DMSC, LOGL_ERROR, "Failed to create the SMS queue.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-05-17 11:31:14 +00:00
|
|
|
sms->cfg = network->sms_queue_cfg;
|
2022-05-15 11:03:57 +00:00
|
|
|
sms->statg = osmo_stat_item_group_alloc(sms, &smsq_statg_desc, 0);
|
|
|
|
if (!sms->statg)
|
|
|
|
goto err_free;
|
|
|
|
|
|
|
|
sms->ctrg = rate_ctr_group_alloc(sms, &smsq_ctrg_desc, 0);
|
|
|
|
if (!sms->ctrg)
|
|
|
|
goto err_statg;
|
2010-12-24 20:39:55 +00:00
|
|
|
|
2010-12-24 12:48:27 +00:00
|
|
|
network->sms_queue = sms;
|
2010-12-24 20:39:55 +00:00
|
|
|
INIT_LLIST_HEAD(&sms->pending_sms);
|
2010-12-24 12:48:27 +00:00
|
|
|
sms->network = network;
|
2017-05-08 18:57:52 +00:00
|
|
|
osmo_timer_setup(&sms->push_queue, sms_submit_pending, sms);
|
|
|
|
osmo_timer_setup(&sms->resend_pending, sms_resend_pending, sms);
|
2010-12-24 20:39:55 +00:00
|
|
|
|
2022-05-15 11:03:57 +00:00
|
|
|
osmo_signal_register_handler(SS_SUBSCR, sms_subscr_cb, network);
|
|
|
|
osmo_signal_register_handler(SS_SMS, sms_sms_cb, network);
|
|
|
|
|
2022-05-17 11:31:14 +00:00
|
|
|
if (db_init(sms, sms->cfg->db_file_path, true)) {
|
|
|
|
LOGP(DMSC, LOGL_FATAL, "DB: Failed to init database: %s\n",
|
|
|
|
osmo_quote_str(sms->cfg->db_file_path, -1));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (db_prepare()) {
|
|
|
|
LOGP(DMSC, LOGL_FATAL, "DB: Failed to prepare database.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-12-24 20:39:55 +00:00
|
|
|
sms_submit_pending(sms);
|
2010-12-24 12:48:27 +00:00
|
|
|
|
|
|
|
return 0;
|
2022-05-15 11:03:57 +00:00
|
|
|
|
|
|
|
err_statg:
|
|
|
|
osmo_stat_item_group_free(sms->statg);
|
|
|
|
err_free:
|
|
|
|
talloc_free(sms);
|
|
|
|
|
|
|
|
return -ENOMEM;
|
2010-12-24 12:48:27 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* call-back: Given subscriber is now ready for short messages. */
|
2016-06-19 16:06:02 +00:00
|
|
|
static int sub_ready_for_sm(struct gsm_network *net, struct vlr_subscr *vsub)
|
2010-12-24 12:48:27 +00:00
|
|
|
{
|
|
|
|
struct gsm_sms *sms;
|
2011-07-24 22:13:06 +00:00
|
|
|
struct gsm_sms_pending *pending;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The code used to be very clever and tried to submit
|
|
|
|
* a SMS during the Location Updating Request. This has
|
|
|
|
* two issues:
|
|
|
|
* 1.) The Phone might not be ready yet, e.g. the C155
|
|
|
|
* will not respond to the Submit when it is booting.
|
|
|
|
* 2.) The queue is already trying to submit SMS to the
|
|
|
|
* user and by not responding to the paging request
|
|
|
|
* we will set the LAC back to 0. We would have to
|
|
|
|
* stop the paging and move things over.
|
|
|
|
*
|
|
|
|
* We need to be careful in what we try here.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* check if we have pending requests */
|
2016-06-19 16:06:02 +00:00
|
|
|
pending = sms_subscriber_find_pending(net->sms_queue, vsub);
|
2011-07-24 22:13:06 +00:00
|
|
|
if (pending) {
|
|
|
|
LOGP(DMSC, LOGL_NOTICE,
|
|
|
|
"Pending paging while subscriber %llu attached.\n",
|
2016-06-19 16:06:02 +00:00
|
|
|
vsub->id);
|
2011-07-24 22:13:06 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2010-12-24 12:48:27 +00:00
|
|
|
|
2011-07-24 22:13:06 +00:00
|
|
|
/* Now try to deliver any pending SMS to this sub */
|
2022-05-14 13:35:49 +00:00
|
|
|
sms = db_sms_get_unsent_for_subscr(vsub, INT_MAX);
|
2010-12-24 12:48:27 +00:00
|
|
|
if (!sms)
|
|
|
|
return -1;
|
2018-11-22 08:42:39 +00:00
|
|
|
|
2022-05-15 11:03:57 +00:00
|
|
|
_gsm411_send_sms(net, vsub, sms);
|
2010-12-24 12:48:27 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* call-back for SS_SUBSCR signals */
|
2010-12-24 12:48:27 +00:00
|
|
|
static int sms_subscr_cb(unsigned int subsys, unsigned int signal,
|
|
|
|
void *handler_data, void *signal_data)
|
|
|
|
{
|
2016-06-19 16:06:02 +00:00
|
|
|
struct vlr_subscr *vsub = signal_data;
|
2010-12-24 12:48:27 +00:00
|
|
|
|
|
|
|
if (signal != S_SUBSCR_ATTACHED)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* this is readyForSM */
|
2016-06-19 16:06:02 +00:00
|
|
|
return sub_ready_for_sm(handler_data, vsub);
|
2010-12-24 12:48:27 +00:00
|
|
|
}
|
|
|
|
|
2022-05-14 07:54:15 +00:00
|
|
|
/* call-back for SS_SMS signals */
|
2010-12-24 20:39:55 +00:00
|
|
|
static int sms_sms_cb(unsigned int subsys, unsigned int signal,
|
|
|
|
void *handler_data, void *signal_data)
|
2010-12-24 12:48:27 +00:00
|
|
|
{
|
2010-12-24 20:39:55 +00:00
|
|
|
struct gsm_network *network = handler_data;
|
2022-05-17 12:38:45 +00:00
|
|
|
struct gsm_sms_queue *smq = network->sms_queue;
|
2010-12-24 20:39:55 +00:00
|
|
|
struct sms_signal_data *sig_sms = signal_data;
|
|
|
|
struct gsm_sms_pending *pending;
|
2016-06-19 16:06:02 +00:00
|
|
|
struct vlr_subscr *vsub;
|
2010-12-24 20:39:55 +00:00
|
|
|
|
|
|
|
/* We got a new SMS and maybe should launch the queue again. */
|
|
|
|
if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) {
|
2014-02-24 13:29:27 +00:00
|
|
|
/* TODO: For SMMA we might want to re-use the radio connection. */
|
2022-05-17 12:38:45 +00:00
|
|
|
sms_queue_trigger(smq);
|
2010-12-24 20:39:55 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sig_sms->sms)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the entry of our queue. The SMS subsystem will submit
|
|
|
|
* sms that are not in our control as we just have a channel
|
|
|
|
* open anyway.
|
|
|
|
*/
|
2022-05-17 12:38:45 +00:00
|
|
|
pending = sms_find_pending(smq, sig_sms->sms->id);
|
2010-12-24 20:39:55 +00:00
|
|
|
if (!pending)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch (signal) {
|
|
|
|
case S_SMS_DELIVERED:
|
2022-05-17 12:38:45 +00:00
|
|
|
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_ACK);
|
2014-02-24 15:13:04 +00:00
|
|
|
/* Remember the subscriber and clear the pending entry */
|
2019-02-19 01:36:35 +00:00
|
|
|
vsub = pending->vsub;
|
|
|
|
vlr_subscr_get(vsub, __func__);
|
2022-05-17 12:39:51 +00:00
|
|
|
if (smq->cfg->delete_delivered)
|
|
|
|
db_sms_delete_sent_message_by_id(pending->sms_id);
|
2022-05-17 12:38:45 +00:00
|
|
|
sms_pending_free(smq, pending);
|
2014-02-24 15:13:04 +00:00
|
|
|
/* Attempt to send another SMS to this subscriber */
|
2016-06-19 16:06:02 +00:00
|
|
|
sms_send_next(vsub);
|
2019-02-19 01:36:35 +00:00
|
|
|
vlr_subscr_put(vsub, __func__);
|
2010-12-24 20:39:55 +00:00
|
|
|
break;
|
|
|
|
case S_SMS_MEM_EXCEEDED:
|
2022-05-17 12:38:45 +00:00
|
|
|
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_NOMEM);
|
|
|
|
sms_pending_free(smq, pending);
|
|
|
|
sms_queue_trigger(smq);
|
2010-12-24 20:39:55 +00:00
|
|
|
break;
|
|
|
|
case S_SMS_UNKNOWN_ERROR:
|
|
|
|
/*
|
|
|
|
* There can be many reasons for this failure. E.g. the paging
|
|
|
|
* timed out, the subscriber was not paged at all, or there was
|
|
|
|
* a protocol error. The current strategy is to try sending the
|
|
|
|
* next SMS for busy/oom and to retransmit when we have paged.
|
|
|
|
*
|
|
|
|
* When the paging expires three times we will disable the
|
|
|
|
* subscriber. If we have some kind of other transmit error we
|
|
|
|
* should flag the SMS as bad.
|
|
|
|
*/
|
large refactoring: support inter-BSC and inter-MSC Handover
3GPP TS 49.008 '4.3 Roles of MSC-A, MSC-I and MSC-T' defines distinct roles:
- MSC-A is responsible for managing subscribers,
- MSC-I is the gateway to the RAN.
- MSC-T is a second transitory gateway to another RAN during Handover.
After inter-MSC Handover, the MSC-I is handled by a remote MSC instance, while
the original MSC-A retains the responsibility of subscriber management.
MSC-T exists in this patch but is not yet used, since Handover is only prepared
for, not yet implemented.
Facilitate Inter-MSC and inter-BSC Handover by the same internal split of MSC
roles.
Compared to inter-MSC Handover, mere inter-BSC has the obvious simplifications:
- all of MSC-A, MSC-I and MSC-T roles will be served by the same osmo-msc
instance,
- messages between MSC-A and MSC-{I,T} don't need to be routed via E-interface
(GSUP),
- no call routing between MSC-A and -I via MNCC necessary.
This is the largest code bomb I have submitted, ever. Out of principle, I
apologize to everyone trying to read this as a whole. Unfortunately, I see no
sense in trying to split this patch into smaller bits. It would be a huge
amount of work to introduce these changes in separate chunks, especially if
each should in turn be useful and pass all test suites. So, unfortunately, we
are stuck with this code bomb.
The following are some details and rationale for this rather huge refactoring:
* separate MSC subscriber management from ran_conn
struct ran_conn is reduced from the pivotal subscriber management entity it has
been so far to a mere storage for an SCCP connection ID and an MSC subscriber
reference.
The new pivotal subscriber management entity is struct msc_a -- struct msub
lists the msc_a, msc_i, msc_t roles, the vast majority of code paths however
use msc_a, since MSC-A is where all the interesting stuff happens.
Before handover, msc_i is an FSM implementation that encodes to the local
ran_conn. After inter-MSC Handover, msc_i is a compatible but different FSM
implementation that instead forwards via/from GSUP. Same goes for the msc_a
struct: if osmo-msc is the MSC-I "RAN proxy" for a remote MSC-A role, the
msc_a->fi is an FSM implementation that merely forwards via/from GSUP.
* New SCCP implementation for RAN access
To be able to forward BSSAP and RANAP messages via the GSUP interface, the
individual message layers need to be cleanly separated. The IuCS implementation
used until now (iu_client from libosmo-ranap) did not provide this level of
separation, and needed a complete rewrite. It was trivial to implement this in
such a way that both BSSAP and RANAP can be handled by the same SCCP code,
hence the new SCCP-RAN layer also replaces BSSAP handling.
sccp_ran.h: struct sccp_ran_inst provides an abstract handler for incoming RAN
connections. A set of callback functions provides implementation specific
details.
* RAN Abstraction (BSSAP vs. RANAP)
The common SCCP implementation did set the theme for the remaining refactoring:
make all other MSC code paths entirely RAN-implementation-agnostic.
ran_infra.c provides data structures that list RAN implementation specifics,
from logging to RAN de-/encoding to SCCP callbacks and timers. A ran_infra
pointer hence allows complete abstraction of RAN implementations:
- managing connected RAN peers (BSC, RNC) in ran_peer.c,
- classifying and de-/encoding RAN PDUs,
- recording connected LACs and cell IDs and sending out Paging requests to
matching RAN peers.
* RAN RESET now also for RANAP
ran_peer.c absorbs the reset_fsm from a_reset.c; in consequence, RANAP also
supports proper RESET semantics now. Hence osmo-hnbgw now also needs to provide
proper RESET handling, which it so far duly ignores. (TODO)
* RAN de-/encoding abstraction
The RAN abstraction mentioned above serves not only to separate RANAP and BSSAP
implementations transparently, but also to be able to optionally handle RAN on
distinct levels. Before Handover, all RAN messages are handled by the MSC-A
role. However, after an inter-MSC Handover, a standalone MSC-I will need to
decode RAN PDUs, at least in order to manage Assignment of RTP streams between
BSS/RNC and MNCC call forwarding.
ran_msg.h provides a common API with abstraction for:
- receiving events from RAN, i.e. passing RAN decode from the BSC/RNC and
MS/UE: struct ran_dec_msg represents RAN messages decoded from either BSSMAP
or RANAP;
- sending RAN events: ran_enc_msg is the counterpart to compose RAN messages
that should be encoded to either BSSMAP or RANAP and passed down to the
BSC/RNC and MS/UE.
The RAN-specific implementations are completely contained by ran_msg_a.c and
ran_msg_iu.c.
In particular, Assignment and Ciphering have so far been distinct code paths
for BSSAP and RANAP, with switch(via_ran){...} statements all over the place.
Using RAN_DEC_* and RAN_ENC_* abstractions, these are now completely unified.
Note that SGs does not qualify for RAN abstraction: the SGs interface always
remains with the MSC-A role, and SGs messages follow quite distinct semantics
from the fairly similar GERAN and UTRAN.
* MGW and RTP stream management
So far, managing MGW endpoints via MGCP was tightly glued in-between
GSM-04.08-CC on the one and MNCC on the other side. Prepare for switching RTP
streams between different RAN peers by moving to object-oriented
implementations: implement struct call_leg and struct rtp_stream with distinct
FSMs each. For MGW communication, use the osmo_mgcpc_ep API that has originated
from osmo-bsc and recently moved to libosmo-mgcp-client for this purpose.
Instead of implementing a sequence of events with code duplication for the RAN
and CN sides, the idea is to manage each RTP stream separately by firing and
receiving events as soon as codecs and RTP ports are negotiated, and letting
the individual FSMs take care of the MGW management "asynchronously". The
caller provides event IDs and an FSM instance that should be notified of RTP
stream setup progress. Hence it becomes possible to reconnect RTP streams from
one GSM-04.08-CC to another (inter-BSC Handover) or between CC and MNCC RTP
peers (inter-MSC Handover) without duplicating the MGCP code for each
transition.
The number of FSM implementations used for MGCP handling may seem a bit of an
overkill. But in fact, the number of perspectives on RTP forwarding are far
from trivial:
- an MGW endpoint is an entity with N connections, and MGCP "sessions" for
configuring them by talking to the MGW;
- an RTP stream is a remote peer connected to one of the endpoint's
connections, which is asynchronously notified of codec and RTP port choices;
- a call leg is the higher level view on either an MT or MO side of a voice
call, a combination of two RTP streams to forward between two remote peers.
BSC MGW PBX
CI CI
[MGW-endpoint]
[--rtp_stream--] [--rtp_stream--]
[----------------call_leg----------------]
* Use counts
Introduce using the new osmo_use_count API added to libosmocore for this
purpose. Each use token has a distinct name in the logging, which can be a
globally constant name or ad-hoc, like the local __func__ string constant. Use
in the new struct msc_a, as well as change vlr_subscr to the new osmo_use_count
API.
* FSM Timeouts
Introduce using the new osmo_tdef API, which provides a common VTY
implementation for all timer numbers, and FSM state transitions with the
correct timeout. Originated in osmo-bsc, recently moved to libosmocore.
Depends: Ife31e6798b4e728a23913179e346552a7dd338c0 (libosmocore)
Ib9af67b100c4583342a2103669732dab2e577b04 (libosmocore)
Id617265337f09dfb6ddfe111ef5e578cd3dc9f63 (libosmocore)
Ie9e2add7bbfae651c04e230d62e37cebeb91b0f5 (libosmo-sccp)
I26be5c4b06a680f25f19797407ab56a5a4880ddc (osmo-mgw)
Ida0e59f9a1f2dd18efea0a51680a67b69f141efa (osmo-mgw)
I9a3effd38e72841529df6c135c077116981dea36 (osmo-mgw)
Change-Id: I27e4988e0371808b512c757d2b52ada1615067bd
2018-12-07 13:47:34 +00:00
|
|
|
if (sig_sms->paging_result) {
|
2022-05-17 12:38:45 +00:00
|
|
|
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_ERR);
|
2010-12-24 20:39:55 +00:00
|
|
|
/* BAD SMS? */
|
|
|
|
db_sms_inc_deliver_attempts(sig_sms->sms);
|
|
|
|
sms_pending_failed(pending, 0);
|
large refactoring: support inter-BSC and inter-MSC Handover
3GPP TS 49.008 '4.3 Roles of MSC-A, MSC-I and MSC-T' defines distinct roles:
- MSC-A is responsible for managing subscribers,
- MSC-I is the gateway to the RAN.
- MSC-T is a second transitory gateway to another RAN during Handover.
After inter-MSC Handover, the MSC-I is handled by a remote MSC instance, while
the original MSC-A retains the responsibility of subscriber management.
MSC-T exists in this patch but is not yet used, since Handover is only prepared
for, not yet implemented.
Facilitate Inter-MSC and inter-BSC Handover by the same internal split of MSC
roles.
Compared to inter-MSC Handover, mere inter-BSC has the obvious simplifications:
- all of MSC-A, MSC-I and MSC-T roles will be served by the same osmo-msc
instance,
- messages between MSC-A and MSC-{I,T} don't need to be routed via E-interface
(GSUP),
- no call routing between MSC-A and -I via MNCC necessary.
This is the largest code bomb I have submitted, ever. Out of principle, I
apologize to everyone trying to read this as a whole. Unfortunately, I see no
sense in trying to split this patch into smaller bits. It would be a huge
amount of work to introduce these changes in separate chunks, especially if
each should in turn be useful and pass all test suites. So, unfortunately, we
are stuck with this code bomb.
The following are some details and rationale for this rather huge refactoring:
* separate MSC subscriber management from ran_conn
struct ran_conn is reduced from the pivotal subscriber management entity it has
been so far to a mere storage for an SCCP connection ID and an MSC subscriber
reference.
The new pivotal subscriber management entity is struct msc_a -- struct msub
lists the msc_a, msc_i, msc_t roles, the vast majority of code paths however
use msc_a, since MSC-A is where all the interesting stuff happens.
Before handover, msc_i is an FSM implementation that encodes to the local
ran_conn. After inter-MSC Handover, msc_i is a compatible but different FSM
implementation that instead forwards via/from GSUP. Same goes for the msc_a
struct: if osmo-msc is the MSC-I "RAN proxy" for a remote MSC-A role, the
msc_a->fi is an FSM implementation that merely forwards via/from GSUP.
* New SCCP implementation for RAN access
To be able to forward BSSAP and RANAP messages via the GSUP interface, the
individual message layers need to be cleanly separated. The IuCS implementation
used until now (iu_client from libosmo-ranap) did not provide this level of
separation, and needed a complete rewrite. It was trivial to implement this in
such a way that both BSSAP and RANAP can be handled by the same SCCP code,
hence the new SCCP-RAN layer also replaces BSSAP handling.
sccp_ran.h: struct sccp_ran_inst provides an abstract handler for incoming RAN
connections. A set of callback functions provides implementation specific
details.
* RAN Abstraction (BSSAP vs. RANAP)
The common SCCP implementation did set the theme for the remaining refactoring:
make all other MSC code paths entirely RAN-implementation-agnostic.
ran_infra.c provides data structures that list RAN implementation specifics,
from logging to RAN de-/encoding to SCCP callbacks and timers. A ran_infra
pointer hence allows complete abstraction of RAN implementations:
- managing connected RAN peers (BSC, RNC) in ran_peer.c,
- classifying and de-/encoding RAN PDUs,
- recording connected LACs and cell IDs and sending out Paging requests to
matching RAN peers.
* RAN RESET now also for RANAP
ran_peer.c absorbs the reset_fsm from a_reset.c; in consequence, RANAP also
supports proper RESET semantics now. Hence osmo-hnbgw now also needs to provide
proper RESET handling, which it so far duly ignores. (TODO)
* RAN de-/encoding abstraction
The RAN abstraction mentioned above serves not only to separate RANAP and BSSAP
implementations transparently, but also to be able to optionally handle RAN on
distinct levels. Before Handover, all RAN messages are handled by the MSC-A
role. However, after an inter-MSC Handover, a standalone MSC-I will need to
decode RAN PDUs, at least in order to manage Assignment of RTP streams between
BSS/RNC and MNCC call forwarding.
ran_msg.h provides a common API with abstraction for:
- receiving events from RAN, i.e. passing RAN decode from the BSC/RNC and
MS/UE: struct ran_dec_msg represents RAN messages decoded from either BSSMAP
or RANAP;
- sending RAN events: ran_enc_msg is the counterpart to compose RAN messages
that should be encoded to either BSSMAP or RANAP and passed down to the
BSC/RNC and MS/UE.
The RAN-specific implementations are completely contained by ran_msg_a.c and
ran_msg_iu.c.
In particular, Assignment and Ciphering have so far been distinct code paths
for BSSAP and RANAP, with switch(via_ran){...} statements all over the place.
Using RAN_DEC_* and RAN_ENC_* abstractions, these are now completely unified.
Note that SGs does not qualify for RAN abstraction: the SGs interface always
remains with the MSC-A role, and SGs messages follow quite distinct semantics
from the fairly similar GERAN and UTRAN.
* MGW and RTP stream management
So far, managing MGW endpoints via MGCP was tightly glued in-between
GSM-04.08-CC on the one and MNCC on the other side. Prepare for switching RTP
streams between different RAN peers by moving to object-oriented
implementations: implement struct call_leg and struct rtp_stream with distinct
FSMs each. For MGW communication, use the osmo_mgcpc_ep API that has originated
from osmo-bsc and recently moved to libosmo-mgcp-client for this purpose.
Instead of implementing a sequence of events with code duplication for the RAN
and CN sides, the idea is to manage each RTP stream separately by firing and
receiving events as soon as codecs and RTP ports are negotiated, and letting
the individual FSMs take care of the MGW management "asynchronously". The
caller provides event IDs and an FSM instance that should be notified of RTP
stream setup progress. Hence it becomes possible to reconnect RTP streams from
one GSM-04.08-CC to another (inter-BSC Handover) or between CC and MNCC RTP
peers (inter-MSC Handover) without duplicating the MGCP code for each
transition.
The number of FSM implementations used for MGCP handling may seem a bit of an
overkill. But in fact, the number of perspectives on RTP forwarding are far
from trivial:
- an MGW endpoint is an entity with N connections, and MGCP "sessions" for
configuring them by talking to the MGW;
- an RTP stream is a remote peer connected to one of the endpoint's
connections, which is asynchronously notified of codec and RTP port choices;
- a call leg is the higher level view on either an MT or MO side of a voice
call, a combination of two RTP streams to forward between two remote peers.
BSC MGW PBX
CI CI
[MGW-endpoint]
[--rtp_stream--] [--rtp_stream--]
[----------------call_leg----------------]
* Use counts
Introduce using the new osmo_use_count API added to libosmocore for this
purpose. Each use token has a distinct name in the logging, which can be a
globally constant name or ad-hoc, like the local __func__ string constant. Use
in the new struct msc_a, as well as change vlr_subscr to the new osmo_use_count
API.
* FSM Timeouts
Introduce using the new osmo_tdef API, which provides a common VTY
implementation for all timer numbers, and FSM state transitions with the
correct timeout. Originated in osmo-bsc, recently moved to libosmocore.
Depends: Ife31e6798b4e728a23913179e346552a7dd338c0 (libosmocore)
Ib9af67b100c4583342a2103669732dab2e577b04 (libosmocore)
Id617265337f09dfb6ddfe111ef5e578cd3dc9f63 (libosmocore)
Ie9e2add7bbfae651c04e230d62e37cebeb91b0f5 (libosmo-sccp)
I26be5c4b06a680f25f19797407ab56a5a4880ddc (osmo-mgw)
Ida0e59f9a1f2dd18efea0a51680a67b69f141efa (osmo-mgw)
I9a3effd38e72841529df6c135c077116981dea36 (osmo-mgw)
Change-Id: I27e4988e0371808b512c757d2b52ada1615067bd
2018-12-07 13:47:34 +00:00
|
|
|
} else {
|
2022-05-17 12:38:45 +00:00
|
|
|
smsq_rate_ctr_inc(smq, SMSQ_CTR_SMS_DELIVERY_TIMEOUT);
|
2010-12-24 20:39:55 +00:00
|
|
|
sms_pending_failed(pending, 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n",
|
2010-12-24 20:39:55 +00:00
|
|
|
sig_sms->paging_result);
|
|
|
|
}
|
|
|
|
|
2018-01-22 16:31:20 +00:00
|
|
|
/* While here, attempt to remove an expired SMS from the DB. */
|
2022-05-17 12:39:51 +00:00
|
|
|
if (smq->cfg->delete_expired)
|
|
|
|
db_sms_delete_oldest_expired_message();
|
2018-01-22 16:31:20 +00:00
|
|
|
|
2010-12-24 20:39:55 +00:00
|
|
|
return 0;
|
2010-12-24 12:48:27 +00:00
|
|
|
}
|
2010-12-25 13:08:00 +00:00
|
|
|
|
|
|
|
/* VTY helper functions */
|
|
|
|
int sms_queue_stats(struct gsm_sms_queue *smsq, struct vty *vty)
|
|
|
|
{
|
|
|
|
struct gsm_sms_pending *pending;
|
|
|
|
|
|
|
|
vty_out(vty, "SMSqueue with max_pending: %d pending: %d%s",
|
2022-05-17 11:31:14 +00:00
|
|
|
smsq->cfg->max_pending, smsq->pending, VTY_NEWLINE);
|
2010-12-25 13:08:00 +00:00
|
|
|
|
|
|
|
llist_for_each_entry(pending, &smsq->pending_sms, entry)
|
2010-12-27 19:19:48 +00:00
|
|
|
vty_out(vty, " SMS Pending for Subscriber: %llu SMS: %llu Failed: %d.%s",
|
2016-06-19 16:06:02 +00:00
|
|
|
pending->vsub->id, pending->sms_id,
|
2010-12-27 19:19:48 +00:00
|
|
|
pending->failed_attempts, VTY_NEWLINE);
|
2010-12-25 13:08:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2010-12-25 13:25:12 +00:00
|
|
|
|
2010-12-25 13:38:30 +00:00
|
|
|
int sms_queue_clear(struct gsm_sms_queue *smsq)
|
|
|
|
{
|
|
|
|
struct gsm_sms_pending *pending, *tmp;
|
|
|
|
|
|
|
|
llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) {
|
2012-11-10 18:46:58 +00:00
|
|
|
LOGP(DLSMS, LOGL_NOTICE,
|
2016-06-19 16:06:02 +00:00
|
|
|
"SMSqueue clearing for sub %llu\n", pending->vsub->id);
|
2022-05-15 12:55:56 +00:00
|
|
|
sms_pending_free(smsq, pending);
|
2010-12-25 13:38:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|