Change in libosmocore[master]: add osmo_gsup_make_response() and osmo_gsup_message_name_*()

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.org
Sun Nov 24 19:01:33 UTC 2019


neels 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>


More information about the gerrit-log mailing list