Looking at sending GSUP messages between MSCs via an HLR acting as forwarding
agent, I see that the current decision for GSUP message consumption is
suboptimal:
Depending on the message type sent and received, libvlr of osmo-msc forwards
GSUP messages to the MSC code, and there, again, depending on the message type,
specific callbacks get invoked.
See vlr_gsupc_read_cb() and msc_vlr_route_gsup_msg().
In current osmo-msc it might seem to make sense to first resolve the IMSI to a
vlr_subscr in vlr.c. But if osmo-msc acts as a Handover target for an inter-MSC
Handover, it should be able to handle unknown IMSIs. Also, should we ever go
for a separate SMSC process, the VLR as first stage makes no sense. Finding a
vlr_subscr is a one-liner with vlr_subscr_find_by_imsi().
I would much rather have an explicit destination entity advertised in the GSUP
messages, and an explicit common GSUP MUX stage. In other words, the VLR of
osmo-msc shouldn't act as a GSUP forwarder, it should merely be one of the GSUP
consumers, and shouldn't even be involved when the messages are intended for
inter-MSC, for USSD or for SMS use.
And finally, for GSUP error responses, for example a report that a specific
target could not be reached, it may not be possible to trivially derive the
right GSUP message consumer from the GSUP message (like "Routing Error").
Going towards that idea, I have put in place the following in my temporary dev
source tree:
enum osmo_gsup_entity {
OSMO_GSUP_ENTITY_NONE = 0,
OSMO_GSUP_ENTITY_HLR,
OSMO_GSUP_ENTITY_VLR,
OSMO_GSUP_ENTITY_ESME,
OSMO_GSUP_ENTITY_SMSC,
OSMO_GSUP_ENTITY_USSD, // FIXME: what's an "ESME"/"SMSC" for
USSD?
OSMO_GSUP_ENTITY_MSC_A,
OSMO_GSUP_ENTITY_MSC_B,
OSMO_GSUP_ENTITY_COUNT,
};
struct osmo_gsup_message {
[...]
enum osmo_gsup_entity source_entity;
enum osmo_gsup_entity destination_entity;
[...]
};
For calling the right rx_cb, we would need only an explicit target kind, but
for returning errors it is better to also include the source entity kind
explicitly.
A gsup_client_mux API:
struct gsup_client_mux_rx_cb {
int (* func )(struct gsup_client_mux *gcm, void *data, const struct osmo_gsup_message
*msg);
void *data;
};
struct gsup_client_mux {
struct osmo_gsup_client *gsup_client;
/* Target clients by enum osmo_gsup_entity */
struct gsup_client_mux_rx_cb rx_cb[OSMO_GSUP_ENTITY_COUNT];
};
int gsup_client_mux_init(struct gsup_client_mux *gcm, struct osmo_gsup_client
*gsup_client);
int gsup_client_mux_tx(struct gsup_client_mux *gcm, const struct osmo_gsup_message
*gsup_msg);
void gsup_client_mux_tx_error_reply(struct gsup_client_mux *gcm, const struct
osmo_gsup_message *gsup_orig,
enum gsm48_gmm_cause cause);
For backwards compat, we would still need to do target classification by
message type, but only if no explicit destination_entity is set:
static enum osmo_gsup_entity gsup_client_mux_classify(struct gsup_client_mux *gcm,
const struct osmo_gsup_message *gsup)
{
if (gsup->destination_entity)
return gsup->destination_entity;
/* Legacy message that lacks an explicit target entity. Guess by message type for
backwards compat: */
switch (gsup_msg->message_type) {
case OSMO_GSUP_MSGT_PROC_SS_REQUEST:
case OSMO_GSUP_MSGT_PROC_SS_RESULT:
case OSMO_GSUP_MSGT_PROC_SS_ERROR:
return OSMO_GSUP_ENTITY_USSD;
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:
case OSMO_GSUP_MSGT_MT_FORWARD_SM_REQUEST:
return OSMO_GSUP_ENTITY_SMSC;
default:
/* osmo-hlr capable of forwarding inter-MSC messages always includes the target entity,
so any
* other legacy message is for the VLR. */
return OSMO_GSUP_ENTITY_VLR;
}
}
We'd have:
HLR <-> VLR
ESME <-> SMSC
USSD <-> USSD (names??)
MSC_A <-> MSC_B
Thanks for your thoughts.
~N