This is merely a historical archive of years 2008-2021, before the migration to mailman3.
A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.
neels gerrit-no-reply at lists.osmocom.orgneels has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/16189 )
Change subject: add osmo_gsup_make_response() and osmo_gsup_message_name_*()
......................................................................
add osmo_gsup_make_response() and osmo_gsup_message_name_*()
As I am implementing GSUP proxy forwarding between osmo-hlr instances, it
becomes apparent that many code paths omit to copy back essential information
in replies to GSUP requests.
Provide a definitive common function that ensures a response is routed back to
the sender with all message fields reflecting the same subscriber, session
identification and message realm.
It is up to osmo-hlr's GSUP server and all clients to use this function in all
relevant GSUP message composition. The most important user is osmo-hlr, which
for now does all proxy routing tasks for directly attached clients.
However, if a core network entity other than the home HLR of a subscriber
launches an MT request, client programs like osmo-msc should also ensure to use
this function, so that responses are sure to reach the requesting peer.
osmo_gsup_message_name_*() are useful to check the osmo_gsup_make_response()
results in gsup_test.c, and are also otherwise useful for debug logging. So far
it prints exactly those elements required to be copied to a response.
Change-Id: Id9692880079ea0f219f52d81b1923a76fc640566
---
M include/osmocom/gsm/gsup.h
M src/gsm/gsup.c
M src/gsm/libosmogsm.map
M tests/gsup/gsup_test.c
M tests/gsup/gsup_test.ok
5 files changed, 244 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/89/16189/1
diff --git a/include/osmocom/gsm/gsup.h b/include/osmocom/gsm/gsup.h
index c883dfb..bf7b99d 100644
--- a/include/osmocom/gsm/gsup.h
+++ b/include/osmocom/gsm/gsup.h
@@ -203,6 +203,7 @@
#define OSMO_GSUP_IS_MSGT_REQUEST(msgt) (((msgt) & 0b00000011) == 0b00)
#define OSMO_GSUP_IS_MSGT_ERROR(msgt) (((msgt) & 0b00000011) == 0b01)
+#define OSMO_GSUP_TO_MSGT_RESULT(msgt) (((msgt) & 0b11111100) | 0b10)
#define OSMO_GSUP_TO_MSGT_ERROR(msgt) (((msgt) & 0b11111100) | 0b01)
extern const struct value_string osmo_gsup_message_type_names[];
@@ -390,4 +391,10 @@
int osmo_gsup_get_err_msg_type(enum osmo_gsup_message_type type_in)
OSMO_DEPRECATED("Use OSMO_GSUP_TO_MSGT_ERROR() instead");
+int osmo_gsup_make_response(struct osmo_gsup_message *reply,
+ const struct osmo_gsup_message *rx, bool error, bool final_response);
+
+size_t osmo_gsup_message_name_buf(char *buf, size_t bufsize, const struct osmo_gsup_message *msg);
+char *osmo_gsup_message_name_c(void *ctx, const struct osmo_gsup_message *msg);
+
/*! @} */
diff --git a/src/gsm/gsup.c b/src/gsm/gsup.c
index ad7a2a4..b6cfcae 100644
--- a/src/gsm/gsup.c
+++ b/src/gsm/gsup.c
@@ -903,4 +903,140 @@
{}
};
+/*! Set fields that need to be copied from a received message over to its response message.
+ *
+ * Note that fields like reply->destination_name may reference the same memory as rx and are not deep-copied, as usual
+ * when using this GSUP API.
+ *
+ * These fields are copied to the reply message, iff they are still unset:
+ * - Set reply->message_type to the rx's matching RESULT code (or ERROR code if error == true).
+ * - IMSI,
+ * - Set reply->destination_name to rx->source_name (for proxy routing),
+ * - sm_rp_mr (for SMS),
+ * - session_id (for SS/USSD),
+ * - if rx->session_state is not NONE, set tx->session_state depending on the final_response argument:
+ * If false, set to OSMO_GSUP_SESSION_STATE_CONTINUE, else OSMO_GSUP_SESSION_STATE_END.
+ *
+ * If values in reply are already set, they will not be overwritten. The return code is an optional way of finding out
+ * whether all values that were already set in 'reply' are indeed matching the 'rx' values that would have been set.
+ *
+ * \param[in] rx Received GSUP message that is being replied to.
+ * \param[inout] reply The message that should be the response to rx, either empty or with some values already set up.
+ * \return 0 if the resulting message is a valid response for rx, nonzero otherwise. If rc is nonzero, the reply message
+ * will have all fields set as if zero were returned, so it is possible to compose invalid responses by
+ * ignoring the return value (for flexibility). The rc is intended to warn if the reply message already
+ * contained data that is incompatible with rx, e.g. a mismatching IMSI.
+ */
+int osmo_gsup_make_response(struct osmo_gsup_message *reply,
+ const struct osmo_gsup_message *rx, bool error, bool final_response)
+{
+ int rc = 0;
+
+ if (!reply->message_type) {
+ if (error)
+ reply->message_type = OSMO_GSUP_TO_MSGT_ERROR(rx->message_type);
+ else
+ reply->message_type = OSMO_GSUP_TO_MSGT_RESULT(rx->message_type);
+ }
+
+ if (!reply->imsi[0])
+ OSMO_STRLCPY_ARRAY(reply->imsi, rx->imsi);
+
+ if (reply->message_class == OSMO_GSUP_MESSAGE_CLASS_UNSET)
+ reply->message_class = rx->message_class;
+
+ if (!reply->destination_name || !reply->destination_name_len) {
+ reply->destination_name = rx->source_name;
+ reply->destination_name_len = rx->source_name_len;
+ }
+
+ /* RP-Message-Reference is mandatory for SM Service */
+ if (!reply->sm_rp_mr)
+ reply->sm_rp_mr = rx->sm_rp_mr;
+
+ /* For SS/USSD, it's important to keep both session state and ID IEs */
+ if (!reply->session_id)
+ reply->session_id = rx->session_id;
+ if (rx->session_state != OSMO_GSUP_SESSION_STATE_NONE
+ && reply->session_state == OSMO_GSUP_SESSION_STATE_NONE) {
+ if (final_response || rx->session_state == OSMO_GSUP_SESSION_STATE_END)
+ reply->session_state = OSMO_GSUP_SESSION_STATE_END;
+ else
+ reply->session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
+ }
+
+ if (strcmp(reply->imsi, rx->imsi))
+ rc |= 1 << 0;
+ if (reply->message_class != rx->message_class)
+ rc |= 1 << 1;
+ if (rx->sm_rp_mr && (!reply->sm_rp_mr || *rx->sm_rp_mr != *reply->sm_rp_mr))
+ rc |= 1 << 2;
+ if (reply->session_id != rx->session_id)
+ rc |= 1 << 3;
+ return rc;
+}
+
+/*! Print the most important value of a GSUP message to a string buffer in human readable form.
+ * \param[out] buf The buffer to write to.
+ * \param[out] buflen sizeof(buf).
+ * \param[in] msg GSUP message to print.
+ */
+size_t osmo_gsup_message_name_buf(char *buf, size_t buflen, const struct osmo_gsup_message *msg)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ if (!msg) {
+ OSMO_STRBUF_PRINTF(sb, "NULL");
+ return sb.chars_needed;
+ }
+
+ if (msg->message_class)
+ OSMO_STRBUF_PRINTF(sb, "%s ", osmo_gsup_message_class_name(msg->message_class));
+
+ OSMO_STRBUF_PRINTF(sb, "%s:", osmo_gsup_message_type_name(msg->message_type));
+
+ OSMO_STRBUF_PRINTF(sb, " imsi=");
+ OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, msg->imsi, strnlen(msg->imsi, sizeof(msg->imsi)));
+
+ if (msg->cause)
+ OSMO_STRBUF_PRINTF(sb, " cause=%s", get_value_string(gsm48_gmm_cause_names, msg->cause));
+
+ switch (msg->cn_domain) {
+ case OSMO_GSUP_CN_DOMAIN_CS:
+ OSMO_STRBUF_PRINTF(sb, " cn_domain=CS");
+ break;
+ case OSMO_GSUP_CN_DOMAIN_PS:
+ OSMO_STRBUF_PRINTF(sb, " cn_domain=PS");
+ break;
+ default:
+ if (msg->cn_domain)
+ OSMO_STRBUF_PRINTF(sb, " cn_domain=?(%d)", msg->cn_domain);
+ break;
+ }
+
+ if (msg->source_name_len) {
+ OSMO_STRBUF_PRINTF(sb, " source_name=");
+ OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->source_name, msg->source_name_len);
+ }
+
+ if (msg->destination_name_len) {
+ OSMO_STRBUF_PRINTF(sb, " destination_name=");
+ OSMO_STRBUF_APPEND(sb, osmo_quote_cstr_buf, (char*)msg->destination_name, msg->destination_name_len);
+ }
+
+ if (msg->session_id)
+ OSMO_STRBUF_PRINTF(sb, " session_id=%u", msg->session_id);
+ if (msg->session_state)
+ OSMO_STRBUF_PRINTF(sb, " session_state=%s", osmo_gsup_session_state_name(msg->session_state));
+
+ if (msg->sm_rp_mr)
+ OSMO_STRBUF_PRINTF(sb, " sm_rp_mr=%u", *msg->sm_rp_mr);
+
+ return sb.chars_needed;
+}
+
+char *osmo_gsup_message_name_c(void *ctx, const struct osmo_gsup_message *msg)
+{
+ OSMO_NAME_C_IMPL(ctx, 64, "ERROR", osmo_gsup_message_name_buf, msg)
+}
+
/*! @} */
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index a0e3b32..2eb0120 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -602,6 +602,9 @@
osmo_gsup_session_state_names;
osmo_gsup_message_class_names;
osmo_gsup_get_err_msg_type;
+osmo_gsup_make_response;
+osmo_gsup_message_name_buf;
+osmo_gsup_message_name_c;
osmo_gsup_sms_encode_sm_rp_da;
osmo_gsup_sms_decode_sm_rp_da;
diff --git a/tests/gsup/gsup_test.c b/tests/gsup/gsup_test.c
index b84c88f..14ba8f9 100644
--- a/tests/gsup/gsup_test.c
+++ b/tests/gsup/gsup_test.c
@@ -20,6 +20,8 @@
#define TEST_SOURCE_NAME_IE 0x60, 0x05, 'M', 'S', 'C', '-', 'A'
#define TEST_DESTINATION_NAME_IE 0x61, 0x05, 'M', 'S', 'C', '-', 'B'
+void *ctx;
+
static void test_gsup_messages_dec_enc(void)
{
int test_idx;
@@ -727,6 +729,69 @@
}
}
+static void test_gsup_make_response(void)
+{
+ char *source_name = "incoming-source-name";
+ char *destination_name = "preset-destination-name";
+ uint8_t sm_rp_mr = 23;
+ uint8_t other_sm_rp_mr = 17;
+ struct osmo_gsup_message rx = {
+ .message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST,
+ .imsi = "1234567",
+ .message_class = OSMO_GSUP_MESSAGE_CLASS_SUBSCRIBER_MANAGEMENT,
+ .source_name = (uint8_t*)source_name,
+ .source_name_len = strlen(source_name) + 1,
+ .sm_rp_mr = &sm_rp_mr,
+ .session_id = 42,
+ .session_state = OSMO_GSUP_SESSION_STATE_BEGIN,
+ };
+ struct osmo_gsup_message nonempty = {
+ .message_type = OSMO_GSUP_MSGT_ROUTING_ERROR,
+ .imsi = "987654321",
+ .message_class = OSMO_GSUP_MESSAGE_CLASS_INTER_MSC,
+ .destination_name = (uint8_t*)destination_name,
+ .destination_name_len = strlen(destination_name) + 1,
+ .sm_rp_mr = &other_sm_rp_mr,
+ .session_id = 11,
+ .session_state = OSMO_GSUP_SESSION_STATE_END,
+ };
+ void *name_ctx = talloc_named_const(ctx, 0, __func__);
+ int error;
+ int final;
+ char *nonempty_str;
+ int rc;
+
+ printf("\n%s()\n", __func__);
+ printf("rx = %s\n", osmo_gsup_message_name_c(name_ctx, &rx));
+
+ printf("\nwriting to an empty struct osmo_gsup_message should populate values as needed:\n");
+ for (error = 0; error <= 1; error++) {
+ for (final = 0; final <= 1; final++) {
+ struct osmo_gsup_message target = {};
+ printf("- args (error=%d, final=%d)\n", error, final);
+ rc = osmo_gsup_make_response(&target, &rx, error, final);
+ printf(" %s\n", osmo_gsup_message_name_c(name_ctx, &target));
+ printf(" rc = %d\n", rc);
+ }
+ }
+
+ printf("\nwriting to an already populated struct osmo_gsup_message, should have no effect:\n");
+ nonempty_str = osmo_gsup_message_name_c(name_ctx, &nonempty);
+ for (error = 0; error <= 1; error++) {
+ for (final = 0; final <= 1; final++) {
+ struct osmo_gsup_message target = nonempty;
+ char *result;
+ printf("- args (error=%d, final=%d)\n", error, final);
+ rc = osmo_gsup_make_response(&target, &rx, error, final);
+ result = osmo_gsup_message_name_c(name_ctx, &target);
+ printf(" %s\n", result);
+ if (strcmp(result, nonempty_str))
+ printf(" ERROR: expected: %s\n", nonempty_str);
+ printf(" rc = %d\n", rc);
+ }
+ }
+}
+
const struct log_info_cat default_categories[] = {
};
@@ -737,7 +802,7 @@
int main(int argc, char **argv)
{
- void *ctx = talloc_named_const(NULL, 0, "gsup_test");
+ ctx = talloc_named_const(NULL, 0, "gsup_test");
osmo_init_logging2(ctx, &info);
log_set_print_filename(osmo_stderr_target, 0);
log_set_print_timestamp(osmo_stderr_target, 0);
@@ -745,6 +810,7 @@
log_set_print_category(osmo_stderr_target, 1);
test_gsup_messages_dec_enc();
+ test_gsup_make_response();
printf("Done.\n");
return EXIT_SUCCESS;
diff --git a/tests/gsup/gsup_test.ok b/tests/gsup/gsup_test.ok
index db8bc2f..cdbf23f 100644
--- a/tests/gsup/gsup_test.ok
+++ b/tests/gsup/gsup_test.ok
@@ -77,4 +77,35 @@
E Abort OK
Testing E Routing Error
E Routing Error OK
+
+test_gsup_make_response()
+rx = Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST: imsi="1234567" source_name="incoming-source-name\0" session_id=42 session_state=BEGIN sm_rp_mr=23
+
+writing to an empty struct osmo_gsup_message should populate values as needed:
+- args (error=0, final=0)
+ Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=CONTINUE sm_rp_mr=23
+ rc = 0
+- args (error=0, final=1)
+ Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=END sm_rp_mr=23
+ rc = 0
+- args (error=1, final=0)
+ Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=CONTINUE sm_rp_mr=23
+ rc = 0
+- args (error=1, final=1)
+ Subscriber-Management OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: imsi="1234567" destination_name="incoming-source-name\0" session_id=42 session_state=END sm_rp_mr=23
+ rc = 0
+
+writing to an already populated struct osmo_gsup_message, should have no effect:
+- args (error=0, final=0)
+ Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17
+ rc = 15
+- args (error=0, final=1)
+ Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17
+ rc = 15
+- args (error=1, final=0)
+ Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17
+ rc = 15
+- args (error=1, final=1)
+ Inter-MSC OSMO_GSUP_MSGT_ROUTING_ERROR: imsi="987654321" destination_name="preset-destination-name\0" session_id=11 session_state=END sm_rp_mr=17
+ rc = 15
Done.
--
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/16189
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Id9692880079ea0f219f52d81b1923a76fc640566
Gerrit-Change-Number: 16189
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20191124/7a4a1dc7/attachment.htm>