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>