From bb299668cbcd06ce553e5eb11d75a4ce87d1bdb5 Mon Sep 17 00:00:00 2001 From: Andrey Velikiy Date: Wed, 19 Jun 2019 14:19:10 +0300 Subject: [PATCH] Add support of the GSUP E Interface messages Encoding and decoding of the following GSUP messages were added to support inter-MSC handover functionality: * E Prepare Handover Request/Result/Error * E Prepare Subsequent Handover Request/Result/Error * E Send End Signal Request/Result/Error * E Forward Access Signalling Request * E Process Access Signalling Request * E Close, E Abort, E Routing Error. Tests for GSUP E Interface messages were added from libosmocore library. --- include/gsup_protocol.hrl | 46 ++++- src/gsup_protocol.erl | 48 ++++++ src/osmo_gsup.app.src | 2 +- test/gsup_encode_decode_test.erl | 286 +++++++++++++++++++++++++++++++ 4 files changed, 378 insertions(+), 4 deletions(-) diff --git a/include/gsup_protocol.hrl b/include/gsup_protocol.hrl index 2b5676e..d6f0235 100644 --- a/include/gsup_protocol.hrl +++ b/include/gsup_protocol.hrl @@ -40,7 +40,21 @@ | ready_for_sm_res | check_imei_req | check_imei_err - | check_imei_res. + | check_imei_res + | e_prepare_handover_req + | e_prepare_handover_err + | e_prepare_handover_res + | e_prepare_subseq_handover_req + | e_prepare_subseq_handover_err + | e_prepare_subseq_handover_res + | e_send_end_signal_req + | e_send_end_signal_err + | e_send_end_signal_res + | e_process_access_signalling_req + | e_forward_access_signalling_req + | e_close + | e_abort + | e_routing_err. -type 'GSUPMessage'() :: #{ message_type := 'GSUPMessageType'(), @@ -84,7 +98,13 @@ sm_alert_reason => integer(), imei => binary(), imei_check_result => integer(), - message_class => integer() + message_class => integer(), + source_name => binary(), + destination_name => binary(), + an_apdu => binary(), + rr_cause => integer(), + session_management_cause => integer(), + bssap_cause => integer() }. -define(SESSION_STATE_BEGIN, 1). @@ -127,6 +147,12 @@ -define(SM_ALERT_REASON, 16#46). -define(IMEI, 16#50). -define(IMEI_CHECK_RESULT, 16#51). +-define(SOURCE_NAME, 16#60). +-define(DESTINATION_NAME, 16#61). +-define(AN_APDU, 16#62). +-define(RR_CAUSE, 16#63). +-define(BSSAP_CAUSE, 16#64). +-define(SESSION_MANAGEMENT_CAUSE, 16#65). -define(MANDATORY_DEFAULT, [imsi, message_type]). @@ -166,7 +192,21 @@ 16#2e => #{message_type => ready_for_sm_res, mandatory => []}, 16#30 => #{message_type => check_imei_req, mandatory => [imei]}, 16#31 => #{message_type => check_imei_err, mandatory => [cause]}, - 16#32 => #{message_type => check_imei_res, mandatory => [imei_check_result]} + 16#32 => #{message_type => check_imei_res, mandatory => [imei_check_result]}, + 16#34 => #{message_type => e_prepare_handover_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#35 => #{message_type => e_prepare_handover_err, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state, bssap_cause]}, + 16#36 => #{message_type => e_prepare_handover_res, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#38 => #{message_type => e_prepare_subseq_handover_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#39 => #{message_type => e_prepare_subseq_handover_err, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state, bssap_cause]}, + 16#3a => #{message_type => e_prepare_subseq_handover_res, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#3c => #{message_type => e_send_end_signal_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#3d => #{message_type => e_send_end_signal_err, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state, bssap_cause]}, + 16#3e => #{message_type => e_send_end_signal_res, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#40 => #{message_type => e_process_access_signalling_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#44 => #{message_type => e_forward_access_signalling_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, + 16#47 => #{message_type => e_close, mandatory => [message_class, source_name, destination_name, session_id, session_state]}, + 16#4b => #{message_type => e_abort, mandatory => [message_class, session_id, session_state, bssap_cause]}, + 16#4e => #{message_type => e_routing_err, mandatory => [message_class, source_name, destination_name, session_id, session_state]} }). -define(AUTH_TUPLE_MANDATORY, [rand, sres, kc]). diff --git a/src/gsup_protocol.erl b/src/gsup_protocol.erl index a8c1754..fe5f38c 100644 --- a/src/gsup_protocol.erl +++ b/src/gsup_protocol.erl @@ -151,6 +151,27 @@ decode_ie(<>, Map) ?CHECK_LEN(imei_check_result, Len, 1, 1), decode_ie(Tail, Map#{imei_check_result => IMEIResult}); +decode_ie(<>, Map) -> + decode_ie(Tail, Map#{source_name => SourceName}); + +decode_ie(<>, Map) -> + decode_ie(Tail, Map#{destination_name => DestName}); + +decode_ie(<>, Map) -> + decode_ie(Tail, Map#{an_apdu => AN_APDU}); + +decode_ie(<>, Map) -> + ?CHECK_LEN(rr_cause, Len, 1, 1), + decode_ie(Tail, Map#{rr_cause => RRCause}); + +decode_ie(<>, Map) -> + ?CHECK_LEN(bssap_cause, Len, 1, 1), + decode_ie(Tail, Map#{bssap_cause => BSSAPCause}); + +decode_ie(<>, Map) -> + ?CHECK_LEN(session_management_cause, Len, 1, 1), + decode_ie(Tail, Map#{session_management_cause => SMCause}); + decode_ie(<<_, Len, _:Len/binary, Tail/binary>>, Map) -> %% skip unknown IE decode_ie(Tail, Map); @@ -415,6 +436,33 @@ encode_ie(#{imei_check_result := Value} = GSUPMessage, Head) -> ?CHECK_SIZE(imei_check_result, Len, Value), encode_ie(maps:without([imei_check_result], GSUPMessage), <>); +encode_ie(#{source_name := Value} = GSUPMessage, Head) -> + Len = size(Value), + encode_ie(maps:without([source_name], GSUPMessage), <>); + +encode_ie(#{destination_name := Value} = GSUPMessage, Head) -> + Len = size(Value), + encode_ie(maps:without([destination_name], GSUPMessage), <>); + +encode_ie(#{an_apdu := Value} = GSUPMessage, Head) -> + Len = size(Value), + encode_ie(maps:without([an_apdu], GSUPMessage), <>); + +encode_ie(#{rr_cause := Value} = GSUPMessage, Head) -> + Len = 1, + ?CHECK_SIZE(rr_cause, Len, Value), + encode_ie(maps:without([rr_cause], GSUPMessage), <>); + +encode_ie(#{bssap_cause := Value} = GSUPMessage, Head) -> + Len = 1, + ?CHECK_SIZE(bssap_cause, Len, Value), + encode_ie(maps:without([bssap_cause], GSUPMessage), <>); + +encode_ie(#{session_management_cause := Value} = GSUPMessage, Head) -> + Len = 1, + ?CHECK_SIZE(session_management_cause, Len, Value), + encode_ie(maps:without([session_management_cause], GSUPMessage), <>); + encode_ie(_, Head) -> Head. encode_bcd(BCDNumber) -> encode_bcd(BCDNumber, <<>>). diff --git a/src/osmo_gsup.app.src b/src/osmo_gsup.app.src index cf9f18b..e93b1e3 100644 --- a/src/osmo_gsup.app.src +++ b/src/osmo_gsup.app.src @@ -1,6 +1,6 @@ {application, osmo_gsup, [ {description, "OSMOCOM GSUP library"}, - {vsn, "0.1.1"}, + {vsn, "0.2.0"}, {registered, []}, {applications, [ kernel, diff --git a/test/gsup_encode_decode_test.erl b/test/gsup_encode_decode_test.erl index f7daa36..9fcba6c 100644 --- a/test/gsup_encode_decode_test.erl +++ b/test/gsup_encode_decode_test.erl @@ -11,6 +11,11 @@ -define(TEST_IMSI_IE, 16#01, 16#08, 16#21, 16#43, 16#65, 16#87, 16#09, 16#21, 16#43, 16#f5). -define(TEST_MSISDN_IE, 16#08, 16#07, 16#91, 16#94, 16#61, 16#46, 16#32, 16#24, 16#43). -define(TEST_CLASS_SUBSCR_IE, 16#0a, 16#01, 16#01). +-define(TEST_CLASS_INTER_MSC_IE, 16#0a, 16#01, 16#04). +-define(TEST_AN_APDU_IE, 16#62, 16#05, 16#01, 16#42, 16#42, 16#42, 16#42). +-define(TEST_SOURCE_NAME_IE, 16#60, 16#05, "MSC-A"). +-define(TEST_DESTINATION_NAME_IE, 16#61, 16#05, "MSC-B"). + missing_params_test() -> ?assertError({mandatory_ie_missing,location_cancellation_err,[cause]}, gsup_protocol:decode(<<16#1d, ?TEST_IMSI_IE>>)), @@ -394,3 +399,284 @@ check_imei_res_test() -> ?assertEqual(Map, gsup_protocol:decode(Bin)), ?assertEqual(Bin, gsup_protocol:encode(Map)). +e_prepare_handover_req_test() -> + Bin = <<16#34, ?TEST_IMSI_IE, + %% Session ID and state (begin) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#01, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_prepare_handover_req, + session_id => 3735928559,session_state => 1, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_prepare_handover_err_test() -> + Bin = <<16#35, ?TEST_IMSI_IE, + %% Session ID and state (continue) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#02, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE, %% (Handover Request) ??? unknown IE + %% cause bssap + 16#64, 16#01, 16#51 + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + bssap_cause => 81,destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_prepare_handover_err, + session_id => 3735928559,session_state => 2, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_prepare_handover_res_test() -> + Bin = <<16#36, ?TEST_IMSI_IE, + %% Session ID and state (continue) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#02, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_prepare_handover_res, + session_id => 3735928559,session_state => 2, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_prepare_subseq_handover_req_test() -> + Bin = <<16#38, ?TEST_IMSI_IE, + %% Session ID and state (begin) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#01, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_prepare_subseq_handover_req, + session_id => 3735928559,session_state => 1, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_prepare_subseq_handover_err_test() -> + Bin = <<16#39, ?TEST_IMSI_IE, + %% Session ID and state (continue) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#02, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE, %% (Handover Request) ??? unknown IE + %% cause bssap + 16#64, 16#01, 16#51 + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + bssap_cause => 81,destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_prepare_subseq_handover_err, + session_id => 3735928559,session_state => 2, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_prepare_subseq_handover_res_test() -> + Bin = <<16#3a, ?TEST_IMSI_IE, + %% Session ID and state (continue) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#02, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_prepare_subseq_handover_res, + session_id => 3735928559,session_state => 2, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_send_end_signal_req_test() -> + Bin = <<16#3c, ?TEST_IMSI_IE, + %% Session ID and state (end) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#03, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_send_end_signal_req, + session_id => 3735928559,session_state => 3, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_send_end_signal_err_test() -> + Bin = <<16#3d, ?TEST_IMSI_IE, + %% Session ID and state (end) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#03, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE, %% (Handover Request) ??? unknown IE + %% cause bssap + 16#64, 16#01, 16#51 + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + bssap_cause => 81,destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_send_end_signal_err, + session_id => 3735928559,session_state => 3, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_send_end_signal_res_test() -> + Bin = <<16#3e, ?TEST_IMSI_IE, + %% Session ID and state (end) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#03, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_send_end_signal_res, + session_id => 3735928559,session_state => 3, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_process_access_signalling_req_test() -> + Bin = <<16#40, ?TEST_IMSI_IE, + %% Session ID and state (continue) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#02, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_process_access_signalling_req, + session_id => 3735928559,session_state => 2, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_forward_access_signalling_req_test() -> + Bin = <<16#44, ?TEST_IMSI_IE, + %% Session ID and state (continue) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#02, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE, + ?TEST_AN_APDU_IE %% (Handover Request) + >>, + Map = #{an_apdu => <<1,66,66,66,66>>, + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_forward_access_signalling_req, + session_id => 3735928559,session_state => 2, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_close_test() -> + Bin = <<16#47, ?TEST_IMSI_IE, + %% Session ID and state (end) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#03, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE + >>, + Map = #{ + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_close, + session_id => 3735928559,session_state => 3, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_abort_test() -> + Bin = <<16#4b, ?TEST_IMSI_IE, + %% Session ID and state (end) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#03, + + ?TEST_CLASS_INTER_MSC_IE, + %% cause bssap + 16#64, 16#01, 16#51 + >>, + Map = #{ + imsi => <<"123456789012345">>,message_class => 4, + bssap_cause => 81,message_type => e_abort, + session_id => 3735928559,session_state => 3}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). + +e_routing_error_test() -> + Bin = <<16#4e, ?TEST_IMSI_IE, + %% Session ID and state (end) + 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef, + 16#31, 16#01, 16#03, + + ?TEST_CLASS_INTER_MSC_IE, + ?TEST_SOURCE_NAME_IE, + ?TEST_DESTINATION_NAME_IE + >>, + Map = #{ + destination_name => <<"MSC-B">>, + imsi => <<"123456789012345">>,message_class => 4, + message_type => e_routing_err, + session_id => 3735928559,session_state => 3, + source_name => <<"MSC-A">>}, + ?assertEqual(Map, gsup_protocol:decode(Bin)), + ?assertEqual(Bin, gsup_protocol:encode(Map)). +