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.
This commit is contained in:
Andrey Velikiy 2019-06-19 14:19:10 +03:00 committed by kluchnikov
parent 6df26d10d7
commit bb299668cb
4 changed files with 378 additions and 4 deletions

View File

@ -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]).

View File

@ -151,6 +151,27 @@ decode_ie(<<?IMEI_CHECK_RESULT, Len, IMEIResult:Len/unit:8, Tail/binary>>, Map)
?CHECK_LEN(imei_check_result, Len, 1, 1),
decode_ie(Tail, Map#{imei_check_result => IMEIResult});
decode_ie(<<?SOURCE_NAME, Len, SourceName:Len/binary, Tail/binary>>, Map) ->
decode_ie(Tail, Map#{source_name => SourceName});
decode_ie(<<?DESTINATION_NAME, Len, DestName:Len/binary, Tail/binary>>, Map) ->
decode_ie(Tail, Map#{destination_name => DestName});
decode_ie(<<?AN_APDU, Len, AN_APDU:Len/binary, Tail/binary>>, Map) ->
decode_ie(Tail, Map#{an_apdu => AN_APDU});
decode_ie(<<?RR_CAUSE, Len, RRCause:Len/unit:8, Tail/binary>>, Map) ->
?CHECK_LEN(rr_cause, Len, 1, 1),
decode_ie(Tail, Map#{rr_cause => RRCause});
decode_ie(<<?BSSAP_CAUSE, Len, BSSAPCause:Len/unit:8, Tail/binary>>, Map) ->
?CHECK_LEN(bssap_cause, Len, 1, 1),
decode_ie(Tail, Map#{bssap_cause => BSSAPCause});
decode_ie(<<?SESSION_MANAGEMENT_CAUSE, Len, SMCause:Len/unit:8, Tail/binary>>, 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), <<Head/binary, ?IMEI_CHECK_RESULT, Len, Value:Len/unit:8>>);
encode_ie(#{source_name := Value} = GSUPMessage, Head) ->
Len = size(Value),
encode_ie(maps:without([source_name], GSUPMessage), <<Head/binary, ?SOURCE_NAME, Len, Value/binary>>);
encode_ie(#{destination_name := Value} = GSUPMessage, Head) ->
Len = size(Value),
encode_ie(maps:without([destination_name], GSUPMessage), <<Head/binary, ?DESTINATION_NAME, Len, Value/binary>>);
encode_ie(#{an_apdu := Value} = GSUPMessage, Head) ->
Len = size(Value),
encode_ie(maps:without([an_apdu], GSUPMessage), <<Head/binary, ?AN_APDU, Len, Value/binary>>);
encode_ie(#{rr_cause := Value} = GSUPMessage, Head) ->
Len = 1,
?CHECK_SIZE(rr_cause, Len, Value),
encode_ie(maps:without([rr_cause], GSUPMessage), <<Head/binary, ?RR_CAUSE, Len, Value:Len/unit:8>>);
encode_ie(#{bssap_cause := Value} = GSUPMessage, Head) ->
Len = 1,
?CHECK_SIZE(bssap_cause, Len, Value),
encode_ie(maps:without([bssap_cause], GSUPMessage), <<Head/binary, ?BSSAP_CAUSE, Len, Value:Len/unit:8>>);
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), <<Head/binary, ?SESSION_MANAGEMENT_CAUSE, Len, Value:Len/unit:8>>);
encode_ie(_, Head) -> Head.
encode_bcd(BCDNumber) -> encode_bcd(BCDNumber, <<>>).

View File

@ -1,6 +1,6 @@
{application, osmo_gsup, [
{description, "OSMOCOM GSUP library"},
{vsn, "0.1.1"},
{vsn, "0.2.0"},
{registered, []},
{applications, [
kernel,

View File

@ -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)).