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