Fix bugs in the GSUP library code and add tests from libosmocore library
A number of bugs were found in the initial implementation and fixed during the first real tests and after the tests from libosmocom library were added to the code.
diff --git a/include/gsup_protocol.hrl b/include/gsup_protocol.hrl
index 37a183c..2b5676e 100644
--- a/include/gsup_protocol.hrl
+++ b/include/gsup_protocol.hrl
@@ -135,12 +135,12 @@
-define (GSUP_MESSAGES(), #{
16#04 => #{message_type => location_upd_req, mandatory => [], optional => [cn_domain]},
16#05 => #{message_type => location_upd_err, mandatory => [cause]},
- 16#06 => #{message_type => location_upd_res, mandatory => [], optional => [msisdn, hlr_number, pdp_info_complete, pdp_info_list]},
+ 16#06 => #{message_type => location_upd_res, mandatory => [], optional => [msisdn, hlr_number, pdp_info_complete, pdp_info_list, pdp_charging]},
16#08 => #{message_type => send_auth_info_req, mandatory => [], optional => [cn_domain, auts, rand]},
16#09 => #{message_type => send_auth_info_err, mandatory => [cause]},
- 16#0a => #{message_type => send_auth_info_res, mandatory => [], optional => [auth_tuples]},
+ 16#0a => #{message_type => send_auth_info_res, mandatory => [], optional => [auth_tuples, auts, rand]},
16#0b => #{message_type => auth_failure_report, mandatory => [], optional => [cn_domain]},
- 16#0c => #{message_type => purge_ms_req, mandatory => [hlr_number], optional => [cn_domain]},
+ 16#0c => #{message_type => purge_ms_req, mandatory => [], optional => [cn_domain, hlr_number]},
16#0d => #{message_type => purge_ms_err, mandatory => [cause]},
16#0e => #{message_type => purge_ms_res, mandatory => [freeze_p_tmsi]},
16#10 => #{message_type => insert_sub_data_req, mandatory => [pdp_info_complete], optional => [cn_domain, msisdn, hlr_number, pdp_info_list, pdp_charging]},
@@ -157,13 +157,13 @@
16#22 => #{message_type => ss_res, mandatory => [session_id, session_state], optional => [ss_info]},
16#24 => #{message_type => mo_forward_req, mandatory => [sm_rp_mr, sm_rp_da, sm_rp_oa, sm_rp_ui]},
16#25 => #{message_type => mo_forward_err, mandatory => [sm_rp_mr, sm_rp_cause], optional => [sm_rp_ui]},
- 16#26 => #{message_type => mo_forward_res, mandatory => [sm_rp_mr]},
+ 16#26 => #{message_type => mo_forward_res, mandatory => [sm_rp_mr], optional => [sm_rp_ui]},
16#28 => #{message_type => mt_forward_req, mandatory => [sm_rp_mr, sm_rp_da, sm_rp_oa, sm_rp_ui], optional => [sm_rp_mms]},
16#29 => #{message_type => mt_forward_err, mandatory => [sm_rp_mr, sm_rp_cause], optional => [sm_rp_ui]},
- 16#2a => #{message_type => mt_forward_res, mandatory => [sm_rp_mr]},
- 16#2c => #{message_type => ready_for_sm_req, mandatory => [sm_rp_mr, sm_alert_reason]},
- 16#2d => #{message_type => ready_for_sm_err, mandatory => [sm_rp_mr, sm_sm_rp_cause], optional => [sm_rp_ui]},
- 16#2e => #{message_type => ready_for_sm_res, mandatory => [sm_rp_mr]},
+ 16#2a => #{message_type => mt_forward_res, mandatory => [sm_rp_mr], optional => [sm_rp_ui]},
+ 16#2c => #{message_type => ready_for_sm_req, mandatory => [sm_alert_reason]},
+ 16#2d => #{message_type => ready_for_sm_err, mandatory => [sm_rp_cause], optional => [sm_rp_ui]},
+ 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]}
diff --git a/src/gsup_protocol.erl b/src/gsup_protocol.erl
index 92dfb05..a8c1754 100644
--- a/src/gsup_protocol.erl
+++ b/src/gsup_protocol.erl
@@ -96,11 +96,11 @@
?CHECK_LEN(pdp_charging, Len, 2, 2),
decode_ie(Tail, Map#{pdp_charging => PDPCharging});
-decode_ie(<<?RAND, Len, Rand:Len/unit:8, Tail/binary>>, Map) ->
+decode_ie(<<?RAND, Len, Rand:Len/binary, Tail/binary>>, Map) ->
?CHECK_LEN(rand, Len, 16, 16),
decode_ie(Tail, Map#{rand => Rand});
-decode_ie(<<?AUTS, Len, AUTS:Len/unit:8, Tail/binary>>, Map) ->
+decode_ie(<<?AUTS, Len, AUTS:Len/binary, Tail/binary>>, Map) ->
?CHECK_LEN(auts, Len, 14, 14),
decode_ie(Tail, Map#{auts => AUTS});
@@ -287,6 +287,16 @@
end || Tuple <- Tuples0>>,
encode_ie(maps:without([auth_tuples], GSUPMessage), <<Head/binary, Tuples/binary>>);
+encode_ie(#{msisdn := Value} = GSUPMessage, Head) ->
+ Len = size(Value),
+ ?CHECK_LEN(msisdn, Len, 0, 8),
+ encode_ie(maps:without([msisdn], GSUPMessage), <<Head/binary, ?MSISDN, Len, Value/binary>>);
+
+encode_ie(#{hlr_number := Value} = GSUPMessage, Head) ->
+ Len = size(Value),
+ ?CHECK_LEN(hlr_number, Len, 0, 8),
+ encode_ie(maps:without([hlr_number], GSUPMessage), <<Head/binary, ?HLR_NUMBER, Len, Value/binary>>);
+
encode_ie(#{pdp_info_complete := true} = GSUPMessage, Head) ->
encode_ie(maps:without([pdp_info_complete], GSUPMessage), <<Head/binary, ?PDP_INFO_COMPLETE, 0>>);
@@ -315,15 +325,15 @@
encode_ie(#{freeze_p_tmsi := _} = _GSUPMessage, _Head) ->
error(freeze_p_tmsi_must_be_true);
-encode_ie(#{msisdn := Value} = GSUPMessage, Head) ->
- Len = size(Value),
- ?CHECK_LEN(msisdn, Len, 0, 8),
- encode_ie(maps:without([msisdn], GSUPMessage), <<Head/binary, ?MSISDN, Len, Value/binary>>);
+encode_ie(#{session_id := Value} = GSUPMessage, Head) ->
+ Len = 4,
+ ?CHECK_SIZE(session_id, Len, Value),
+ encode_ie(maps:without([session_id], GSUPMessage), <<Head/binary, ?SESSION_ID, Len, Value:Len/unit:8>>);
-encode_ie(#{hlr_number := Value} = GSUPMessage, Head) ->
- Len = size(Value),
- ?CHECK_LEN(hlr_number, Len, 0, 8),
- encode_ie(maps:without([hlr_number], GSUPMessage), <<Head/binary, ?HLR_NUMBER, Len, Value/binary>>);
+encode_ie(#{session_state := Value} = GSUPMessage, Head) ->
+ Len = 1,
+ ?CHECK_SIZE(session_state, Len, Value),
+ encode_ie(maps:without([session_state], GSUPMessage), <<Head/binary, ?SESSION_STATE, Len, Value:Len/unit:8>>);
encode_ie(#{message_class := Value} = GSUPMessage, Head) ->
Len = 1,
@@ -344,31 +354,21 @@
?CHECK_SIZE(pdp_charging, Len, Value),
encode_ie(maps:without([pdp_charging], GSUPMessage), <<Head/binary, ?PDP_CHARGING, Len, Value:Len/unit:8>>);
-encode_ie(#{rand := Value} = GSUPMessage, Head) ->
- Len = 16,
- ?CHECK_LEN(rand, size(Value), Len, Len),
- encode_ie(maps:without([rand], GSUPMessage), <<Head/binary, ?RAND, Len, Value:Len/unit:8>>);
-
encode_ie(#{auts := Value} = GSUPMessage, Head) ->
Len = 14,
?CHECK_LEN(auts, size(Value), Len, Len),
- encode_ie(maps:without([auts], GSUPMessage), <<Head/binary, ?AUTS, Len, Value:Len/unit:8>>);
+ encode_ie(maps:without([auts], GSUPMessage), <<Head/binary, ?AUTS, Len, Value:Len/binary>>);
+
+encode_ie(#{rand := Value} = GSUPMessage, Head) ->
+ Len = 16,
+ ?CHECK_LEN(rand, size(Value), Len, Len),
+ encode_ie(maps:without([rand], GSUPMessage), <<Head/binary, ?RAND, Len, Value:Len/binary>>);
encode_ie(#{cn_domain := Value} = GSUPMessage, Head) ->
Len = 1,
?CHECK_SIZE(cn_domain, Len, Value),
encode_ie(maps:without([cn_domain], GSUPMessage), <<Head/binary, ?CN_DOMAIN, Len, Value:Len/unit:8>>);
-encode_ie(#{session_id := Value} = GSUPMessage, Head) ->
- Len = 4,
- ?CHECK_SIZE(session_id, Len, Value),
- encode_ie(maps:without([session_id], GSUPMessage), <<Head/binary, ?SESSION_ID, Len, Value:Len/unit:8>>);
-
-encode_ie(#{session_state := Value} = GSUPMessage, Head) ->
- Len = 1,
- ?CHECK_SIZE(session_state, Len, Value),
- encode_ie(maps:without([session_state], GSUPMessage), <<Head/binary, ?SESSION_STATE, Len, Value:Len/unit:8>>);
-
encode_ie(#{ss_info := Value} = GSUPMessage, Head) ->
Len = size(Value),
encode_ie(maps:without([ss_info], GSUPMessage), <<Head/binary, ?SS_INFO, Len, Value/binary>>);
@@ -407,6 +407,7 @@
encode_ie(#{imei := Value} = GSUPMessage, Head) ->
Len = size(Value),
+ ?CHECK_LEN(imei, Len, 9, 9),
encode_ie(maps:without([imei], GSUPMessage), <<Head/binary, ?IMEI, Len, Value/binary>>);
encode_ie(#{imei_check_result := Value} = GSUPMessage, Head) ->
diff --git a/src/ipa.erl b/src/ipa.erl
index 479860e..0e0e6d2 100644
--- a/src/ipa.erl
+++ b/src/ipa.erl
@@ -10,7 +10,7 @@
-export ([decode/1, encode/1]).
--spec decode(binary()) -> {ok, binary()} | {reply, ping | resp | ack, binary(), binary()} | {more_data, binary()} | {error, term()}.
+-spec decode(binary()) -> {ok, {binary(), binary()}} | {reply, ping | resp | ack, binary(), binary()} | {more_data, binary()} | {error, term()}.
decode(<<1:16, ?IPAC_PROTO_IPACCESS, ?IPAC_MSGT_PING, Rest/binary>>) ->
{reply, ping, <<1:16, ?IPAC_PROTO_IPACCESS, ?IPAC_MSGT_PONG>>, Rest};
@@ -28,7 +28,7 @@
{error, {bad_protocol_extension, X}}
end;
-decode(<<_PSize:16, X, _/binary>>) when X /= ?IPAC_PROTO_OSMO ->
+decode(<<_PSize:16, X, _, _/binary>>) when X /= ?IPAC_PROTO_OSMO ->
{error, {bad_stream_id, X}};
decode(Rest) ->
diff --git a/test/gsup_encode_decode_test.erl b/test/gsup_encode_decode_test.erl
index 52806e8..f7daa36 100644
--- a/test/gsup_encode_decode_test.erl
+++ b/test/gsup_encode_decode_test.erl
@@ -8,55 +8,12 @@
-include_lib("eunit/include/eunit.hrl").
--define(BINARY_ISD_REQUEST_BAD, <<16,1,8,98,66,130,119,116,88,81,242,5,7,16,1,1,18,2,1,42,8,7,6,148,97,49,100,96,33,40,1,1>>).
--define(BINARY_ISD_REQUEST, <<0,35,238,5, 16,1,8,98,66,130,119,116,88,81,242,4,0,5,7,16,1,1,18,2,1,42,8,7,6,148,97,49,100,96,33,40,1,1>>).
--define(MAP_ISD_REQUEST, #{cn_domain => 1,imsi => <<"262428774785152">>,message_type => insert_sub_data_req,msisdn => <<6,148,97,49,100,96,33>>,pdp_info_list => [#{access_point_name => <<1,42>>,pdp_context_id => 1}], pdp_info_complete => true}).
-
--define(BINARY_MO_FORWARD_REQUEST, <<0,44,238,5, 36,1,8,98,66,2,0,0,0,128,248,64,1,66,65,5,3,0,137,103,245,66,8,2,6,148,33,3,0,0,136,67,10,5,35,5,0,33,67,245,0,0,0>>).
--define(MAP_MO_FORWARD_REQUEST, #{imsi => <<"262420000000088">>,message_type => mo_forward_req,sm_rp_da => <<3,0,137,103,245>>,sm_rp_mr => 66,sm_rp_oa => <<2,6,148,33,3,0,0,136>>,sm_rp_ui => <<5,35,5,0,33,67,245,0,0,0>>}).
-
--define(BINARY_SS_REQUEST, <<0,44,238,5, 32,1,8,98,66,2,0,0,0,64,246,48,4,32,0,0,1,49,1,1,53,21,161,19,2,1,5,2,1,59,48,11,4,1,15,4,6,170,81,12,6,27,1>>).
--define(MAP_SS_REQUEST, #{imsi => <<"262420000000046">>,message_type => ss_req,session_id => 536870913,session_state => 1,ss_info => <<161,19,2,1,5,2,1,59,48,11,4,1,15,4,6,170,81,12,6,27,1>>}).
-
--define(BINARY_SAI_RESULT,<<0,192,238,5, 10,1,8,98,66,2,80,118,115,7,240,3,34,32,16,139,144,41,228,197,232,161,115,52,229,66,150,129,111,14,163,33,4,154,221,96,95,34,8,214,95,14,186,82,93,186,131,3,34,32,16,98,45,225,235,92,202,105,88,14,17,66,100,38,60,70,60,33,4,125,216,104,213,34,8,92,188,236,132,7,137,137,207,3,34,32,16,247,184,92,22,164,154,219,122,73,61,217,228,64,22,207,229,33,4,12,236,133,61,34,8,2,247,249,165,41,173,134,71,3,34,32,16,115,152,209,15,231,72,227,254,143,199,185,130,91,206,171,41,33,4,236,133,225,34,34,8,67,180,13,145,7,174,211,12,3,34,32,16,251,173,219,197,60,132,202,24,53,87,236,186,86,175,231,59,33,4,61,86,38,102,34,8,224,104,249,198,53,145,182,54>>).
--define(MAP_SAI_RESULT, #{auth_tuples => [
- #{kc => <<15447081440312670851:8/unit:8>>,rand => <<185511231865796904634040334886313594531:16/unit:8>>,sres => <<2598199391:4/unit:8>>},
- #{kc => <<6682475998917265871:8/unit:8>>,rand => <<130502579135052156657755432460855559740:16/unit:8>>,sres => <<2111334613:4/unit:8>>},
- #{kc => <<213913995087545927:8/unit:8>>,rand => <<329276565356490492799345768940241866725:16/unit:8>>,sres => <<216827197:4/unit:8>>},
- #{kc => <<4878539212899406604:8/unit:8>>,rand => <<153654688921371376697326139997978274601:16/unit:8>>,sres => <<3968196898:4/unit:8>>},
- #{kc => <<16170449091771348534:8/unit:8>>,rand => <<334538951772921257466553732075468351291:16/unit:8>>,sres => <<1029056102:4/unit:8>>}
- ],imsi => <<"262420056737700">>,message_type => send_auth_info_res}).
-
-isd_request_test() ->
- {ok, {Pkt, <<>>}} = ipa:decode(?BINARY_ISD_REQUEST),
- Map = gsup_protocol:decode(Pkt),
- ?assertEqual(?MAP_ISD_REQUEST, Map),
- Bin = ipa:encode(gsup_protocol:encode(Map)),
- ?assertEqual(?BINARY_ISD_REQUEST, Bin).
-
-mo_forward_request_test() ->
- {ok, {Pkt, <<>>}} = ipa:decode(?BINARY_MO_FORWARD_REQUEST),
- Map = gsup_protocol:decode(Pkt),
- ?assertEqual(?MAP_MO_FORWARD_REQUEST, Map),
- Bin = ipa:encode(gsup_protocol:encode(Map)),
- ?assertEqual(?BINARY_MO_FORWARD_REQUEST, Bin).
-
-ss_request_test() ->
- {ok, {Pkt, <<>>}} = ipa:decode(?BINARY_SS_REQUEST),
- Map = gsup_protocol:decode(Pkt),
- ?assertEqual(?MAP_SS_REQUEST, Map),
- Bin = ipa:encode(gsup_protocol:encode(Map)),
- ?assertEqual(?BINARY_SS_REQUEST, Bin).
-
-sai_result_test() ->
- {ok, {Pkt, <<>>}} = ipa:decode(?BINARY_SAI_RESULT),
- Map = gsup_protocol:decode(Pkt),
- ?assertEqual(?MAP_SAI_RESULT, Map),
- Bin = ipa:encode(gsup_protocol:encode(Map)),
- ?assertEqual(?BINARY_SAI_RESULT, Bin).
+-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).
missing_params_test() ->
- ?assertError({mandatory_ie_missing,insert_sub_data_req,[pdp_info_complete]}, gsup_protocol:decode(?BINARY_ISD_REQUEST_BAD)),
+ ?assertError({mandatory_ie_missing,location_cancellation_err,[cause]}, gsup_protocol:decode(<<16#1d, ?TEST_IMSI_IE>>)),
?assertError({mandatory_ie_missing,mo_forward_req,[sm_rp_mr,sm_rp_da,sm_rp_oa,sm_rp_ui]}, gsup_protocol:encode(#{message_type => mo_forward_req, imsi => <<"123456">>})).
excess_params_test() ->
@@ -73,3 +30,367 @@
?assertError({ie_value_length_mismatch,pdp_charging,16#10000}, gsup_protocol:encode_ie(#{pdp_charging => 16#10000}, <<>>)),
?assertEqual(<<48,4,255,255,255,255>>, gsup_protocol:encode_ie(#{session_id => 16#ffffffff}, <<>>)),
?assertError({ie_value_length_mismatch,session_id,16#100000000}, gsup_protocol:encode_ie(#{session_id => 16#100000000}, <<>>)).
+
+sai_req_test() ->
+ Bin = <<16#08, ?TEST_IMSI_IE, ?TEST_CLASS_SUBSCR_IE>>,
+ Map = #{imsi => <<"123456789012345">>, message_class => 1, message_type => send_auth_info_req},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+sai_err_test() ->
+ Bin = <<16#09, ?TEST_IMSI_IE, 16#02, 16#01, 16#07>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => send_auth_info_err, cause=>7},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+sai_res_test() ->
+ Bin = <<16#0a, ?TEST_IMSI_IE,
+ 16#03, 16#22, %% Auth tuple
+ 16#20, 16#10,
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#09, 16#0a, 16#0b, 16#0c, 16#0d, 16#0e, 16#0f, 16#10,
+ 16#21, 16#04,
+ 16#21, 16#22, 16#23, 16#24,
+ 16#22, 16#08,
+ 16#31, 16#32, 16#33, 16#34, 16#35, 16#36, 16#37, 16#38,
+ 16#03, 16#22, %% Auth tuple
+ 16#20, 16#10,
+ 16#81, 16#82, 16#83, 16#84, 16#85, 16#86, 16#87, 16#88,
+ 16#89, 16#8a, 16#8b, 16#8c, 16#8d, 16#8e, 16#8f, 16#90,
+ 16#21, 16#04,
+ 16#a1, 16#a2, 16#a3, 16#a4,
+ 16#22, 16#08,
+ 16#b1, 16#b2, 16#b3, 16#b4, 16#b5, 16#b6, 16#b7, 16#b8
+ >>,
+ Map = #{auth_tuples =>
+ [#{kc => <<16#31, 16#32, 16#33, 16#34, 16#35, 16#36, 16#37, 16#38>>,
+ rand => <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>,
+ sres => <<16#21, 16#22, 16#23, 16#24>>},
+ #{kc => <<16#b1, 16#b2, 16#b3, 16#b4, 16#b5, 16#b6, 16#b7, 16#b8>>,
+ rand =>
+ <<129,130,131,132,133,134,135,136,137,138,139,
+ 140,141,142,143,144>>,
+ sres => <<16#a1, 16#a2, 16#a3, 16#a4>>}],
+ imsi => <<"123456789012345">>,
+ message_type => send_auth_info_res},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+sai_res_umts_test() ->
+ Bin = <<16#0a, ?TEST_IMSI_IE,
+ 16#03, 16#62, %% Auth tuple
+ 16#20, 16#10, %% rand
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#09, 16#0a, 16#0b, 16#0c, 16#0d, 16#0e, 16#0f, 16#10,
+ 16#21, 16#04, %% sres
+ 16#21, 16#22, 16#23, 16#24,
+ 16#22, 16#08, %% kc
+ 16#31, 16#32, 16#33, 16#34, 16#35, 16#36, 16#37, 16#38,
+ 16#23, 16#10, %% IK (UMTS)
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#09, 16#0a, 16#0b, 16#0c, 16#0d, 16#0e, 16#0f, 16#10,
+ 16#24, 16#10, %% CK (UMTS)
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#09, 16#0a, 16#0b, 16#0c, 16#0d, 16#0e, 16#0f, 16#10,
+ 16#25, 16#10, %% AUTN (UMTS)
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#09, 16#0a, 16#0b, 16#0c, 16#0d, 16#0e, 16#0f, 16#10,
+ 16#27, 16#08, %% RES (UMTS)
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#03, 16#62, %% Auth tuple
+ 16#20, 16#10, %% rand
+ 16#a1, 16#a2, 16#a3, 16#a4, 16#a5, 16#a6, 16#a7, 16#a8,
+ 16#a9, 16#aa, 16#ab, 16#ac, 16#ad, 16#ae, 16#af, 16#10,
+ 16#21, 16#04, %% sres
+ 16#b1, 16#b2, 16#b3, 16#b4,
+ 16#22, 16#08, %% kc
+ 16#c1, 16#c2, 16#c3, 16#c4, 16#c5, 16#c6, 16#c7, 16#c8,
+ 16#23, 16#10, %% IK (UMTS)
+ 16#d1, 16#d2, 16#d3, 16#d4, 16#d5, 16#d6, 16#d7, 16#d8,
+ 16#d9, 16#da, 16#db, 16#dc, 16#dd, 16#de, 16#df, 16#d0,
+ 16#24, 16#10, %% CK (UMTS)
+ 16#e1, 16#e2, 16#e3, 16#e4, 16#e5, 16#e6, 16#e7, 16#e8,
+ 16#e9, 16#ea, 16#eb, 16#ec, 16#ed, 16#ee, 16#ef, 16#e0,
+ 16#25, 16#10, %%AUTN (UMTS)
+ 16#f1, 16#f2, 16#f3, 16#f4, 16#f5, 16#f6, 16#f7, 16#f8,
+ 16#f9, 16#fa, 16#fb, 16#fc, 16#fd, 16#fe, 16#ff, 16#f0,
+ 16#27, 16#08, %%RES (UMTS)
+ 16#91, 16#92, 16#93, 16#94, 16#95, 16#96, 16#97, 16#98
+ >>,
+ Map = #{auth_tuples =>
+ [#{autn => <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>,
+ ck => <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>,
+ ik => <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>,
+ kc => <<"12345678">>,
+ rand => <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>,
+ res => <<1,2,3,4,5,6,7,8>>,
+ sres => <<16#21, 16#22, 16#23, 16#24>>},
+ #{autn => <<16#f1, 16#f2, 16#f3, 16#f4, 16#f5, 16#f6, 16#f7, 16#f8,
+ 16#f9, 16#fa, 16#fb, 16#fc, 16#fd, 16#fe, 16#ff, 16#f0>>,
+ ck => <<16#e1, 16#e2, 16#e3, 16#e4, 16#e5, 16#e6, 16#e7, 16#e8,
+ 16#e9, 16#ea, 16#eb, 16#ec, 16#ed, 16#ee, 16#ef, 16#e0>>,
+ ik => <<16#d1, 16#d2, 16#d3, 16#d4, 16#d5, 16#d6, 16#d7, 16#d8,
+ 16#d9, 16#da, 16#db, 16#dc, 16#dd, 16#de, 16#df, 16#d0>>,
+ kc => <<16#c1, 16#c2, 16#c3, 16#c4, 16#c5, 16#c6, 16#c7, 16#c8>>,
+ rand =>
+ <<161,162,163,164,165,166,167,168,169,170,171,
+ 172,173,174,175,16>>,
+ res => <<145,146,147,148,149,150,151,152>>,
+ sres => <<16#b1, 16#b2, 16#b3, 16#b4>>}],
+ imsi => <<"123456789012345">>,
+ message_type => send_auth_info_res},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+sai_res_auts_test() ->
+ Bin = <<16#0a, ?TEST_IMSI_IE,
+ 16#26, 16#0e, %% AUTS (UMTS)
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#09, 16#0a, 16#0b, 16#0c, 16#0d, 16#0e,
+ 16#20, 16#10, %% rand
+ 16#01, 16#02, 16#03, 16#04, 16#05, 16#06, 16#07, 16#08,
+ 16#09, 16#0a, 16#0b, 16#0c, 16#0d, 16#0e, 16#0f, 16#10
+ >>,
+ Map = #{auts => <<1,2,3,4,5,6,7,8,9,10,11,12,13,14>>,
+ imsi => <<"123456789012345">>,
+ message_type => send_auth_info_res,
+ rand => <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+lu_req_test() ->
+ Bin = <<16#04, ?TEST_IMSI_IE>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => location_upd_req},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+lu_err_test() ->
+ Bin = <<16#05, ?TEST_IMSI_IE, 16#02, 16#01, 16#07>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => location_upd_err, cause=>7},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+lu_res_test() ->
+ Bin = <<16#06, ?TEST_IMSI_IE, ?TEST_MSISDN_IE,
+ 16#09, 16#07, %% HLR-Number of the subscriber
+ 16#91, 16#83, 16#52, 16#38, 16#48, 16#83, 16#93,
+ 16#04, 16#00, %% PDP info complete
+ 16#05, 16#19,
+ 16#10, 16#01, 16#01,
+ 16#11, 16#02, 16#f1, 16#21, %% IPv4
+ 16#12, 16#09, 16#04, "test", 16#03, "apn",
+ 16#13, 16#01, 16#02,
+ 16#14, 16#02, 16#FF, 16#23,
+ 16#05, 16#11,
+ 16#10, 16#01, 16#02,
+ 16#11, 16#02, 16#f1, 16#21, %% IPv4
+ 16#12, 16#08, 16#03, "foo", 16#03, "apn",
+ 16#14, 16#02,
+ 16#AE, 16#FF
+ >>,
+ Map = #{hlr_number => <<145,131,82,56,72,131,147>>,
+ imsi => <<"123456789012345">>,
+ message_type => location_upd_res,
+ msisdn => <<145,148,97,70,50,36,67>>,
+ pdp_charging => 44799,pdp_info_complete => true,
+ pdp_info_list =>
+ [#{access_point_name =>
+ <<4,116,101,115,116,3,97,112,110>>,
+ pdp_charging => 65315,pdp_context_id => 1,
+ pdp_type => 61729,
+ quality_of_service => <<2>>},
+ #{access_point_name => <<3,102,111,111,3,97,112,110>>,
+ pdp_context_id => 2,pdp_type => 61729}]},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+lc_req_test() ->
+ Bin = <<16#1c, ?TEST_IMSI_IE, 16#06, 16#01, 16#00>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => location_cancellation_req, cancellation_type => 0},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+lc_err_test() ->
+ Bin = <<16#1d, ?TEST_IMSI_IE, 16#02, 16#01, 16#03>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => location_cancellation_err, cause=>3},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+lc_res_test() ->
+ Bin = <<16#1e, ?TEST_IMSI_IE>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => location_cancellation_res},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+purge_ms_req_test() ->
+ Bin = <<16#0c, ?TEST_IMSI_IE>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => purge_ms_req},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+purge_ms_err_test() ->
+ Bin = <<16#0d, ?TEST_IMSI_IE, 16#02, 16#01, 16#03>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => purge_ms_err, cause=>3},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+purge_ms_res_test() ->
+ Bin = <<16#0e, ?TEST_IMSI_IE, 16#07, 16#00>>,
+ Map = #{imsi => <<"123456789012345">>, message_type => purge_ms_res, freeze_p_tmsi => true},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+% dummy_session_test() ->
+% Bin = <<16#2b, ?TEST_IMSI_IE, %% Session ID and state
+% 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef,
+% 16#31, 16#01, 16#01
+% >>,
+% Map = #{imsi => <<"123456789012345">>},
+% ?assertEqual(Map, gsup_protocol:decode(Bin)),
+% ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+ussd_req_test() ->
+ Bin = <<16#20, ?TEST_IMSI_IE, %% Session ID and state
+ 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef,
+ 16#31, 16#01, 16#01,
+
+ %% SS/USSD information IE
+ 16#35, 16#14,
+ %% ASN.1 encoded MAP payload
+ 16#a1, 16#12,
+ 16#02, 16#01, %% Component: invoke
+ 16#01, %% invokeID = 1
+ %% opCode: processUnstructuredSS-Request
+ 16#02, 16#01, 16#3b, 16#30, 16#0a, 16#04, 16#01, 16#0f,
+ 16#04, 16#05, 16#aa, 16#18, 16#0c, 16#36, 16#02
+ >>,
+ Map = #{imsi => <<"123456789012345">>,message_type => ss_req,
+ session_id => 3735928559,session_state => 1,
+ ss_info =>
+ <<161,18,2,1,1,2,1,59,48,10,4,1,15,4,5,170,24,12,54,2>>},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+ussd_res_test() ->
+ Bin = <<16#22, ?TEST_IMSI_IE, %% Session ID and state
+ 16#30, 16#04, 16#de, 16#ad, 16#be, 16#ef,
+ 16#31, 16#01, 16#03,
+
+ %% SS/USSD information IE
+ 16#35, 16#08,
+ %% ASN.1 encoded MAP payload
+ 16#a3, 16#06,
+ 16#02, 16#01, %% Component: returnError
+ 16#01, %% invokeID = 1
+ %% localValue: unknownAlphabet
+ 16#02, 16#01, 16#47
+ >>,
+ Map = #{imsi => <<"123456789012345">>,message_type => ss_res,
+ session_id => 3735928559,session_state => 3,
+ ss_info => <<163,6,2,1,1,2,1,71>>},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+mo_forward_sm_req_test() ->
+ Bin = <<16#24, ?TEST_IMSI_IE, %% SM related IEs
+ 16#40, 16#01, %% SM-RP-MR (Message Reference)
+ 16#fa,
+ 16#41, 16#08, %% SM-RP-DA (Destination Address)
+ 16#03, %% SMSC address
+ 16#91, 16#52, 16#75, 16#47, 16#99, 16#09, 16#82,
+ 16#42, 16#01, %% SM-RP-OA (Originating Address)
+ 16#ff, %% Special case: noSM-RP-OA
+ 16#43, 16#04, %% SM-RP-UI (TPDU)
+ 16#de, 16#ad, 16#be, 16#ef
+ >>,
+ Map = #{imsi => <<"123456789012345">>,
+ message_type => mo_forward_req,
+ sm_rp_da => <<3,145,82,117,71,153,9,130>>,
+ sm_rp_mr => 250,sm_rp_oa => <<16#ff>>,sm_rp_ui => <<16#de, 16#ad, 16#be, 16#ef>>},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+mt_forward_sm_req_test() ->
+ Bin = <<16#28, ?TEST_IMSI_IE, %% SM related IEs
+ 16#40, 16#01, %% SM-RP-MR (Message Reference)
+ 16#fa,
+ 16#41, 16#09, %% SM-RP-DA (Destination Address)
+ 16#01, %% IMSI
+ 16#21, 16#43, 16#65, 16#87, 16#09, 16#21, 16#43, 16#f5,
+ 16#42, 16#08, %% SM-RP-OA (Originating Address)
+ 16#03, %% SMSC address
+ 16#91, 16#52, 16#75, 16#47, 16#99, 16#09, 16#82,
+ 16#43, 16#04, %% SM-RP-UI (TPDU)
+ 16#de, 16#ad, 16#be, 16#ef,
+ 16#45, 16#01, %% SM-RP-MMS (More Messages to Send)
+ 16#01
+ >>,
+ Map = #{imsi => <<"123456789012345">>,
+ message_type => mt_forward_req,
+ sm_rp_da => <<1,33,67,101,135,9,33,67,245>>,
+ sm_rp_mms => 1,sm_rp_mr => 250,
+ sm_rp_oa => <<3,145,82,117,71,153,9,130>>,
+ sm_rp_ui => <<16#de, 16#ad, 16#be, 16#ef>>},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+mo_forward_sm_err_test() ->
+ Bin = <<16#25, ?TEST_IMSI_IE, %% SM related IEs
+ 16#40, 16#01, %% SM-RP-MR (Message Reference)
+ 16#fa,
+ 16#44, 16#01, %% SM-RP-Cause value
+ 16#af
+ >>,
+ Map = #{imsi => <<"123456789012345">>,
+ message_type => mo_forward_err,sm_rp_cause => 175,
+ sm_rp_mr => 250},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+mt_forward_sm_res_test() ->
+ Bin = <<16#2a, ?TEST_IMSI_IE, %% SM related IEs
+ 16#40, 16#01, %% SM-RP-MR (Message Reference)
+ 16#fa,
+ 16#43, 16#04, %% SM-RP-UI (TPDU)
+ 16#de, 16#ad, 16#be, 16#ef
+ >>,
+ Map = #{imsi => <<"123456789012345">>,
+ message_type => mt_forward_res,sm_rp_mr => 250,
+ sm_rp_ui => <<16#de, 16#ad, 16#be, 16#ef>>},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+ready_for_sm_req_test() ->
+ Bin = <<16#2c, ?TEST_IMSI_IE, 16#46, 16#01, 16#02>>,
+ Map = #{imsi => <<"123456789012345">>,
+ message_type => ready_for_sm_req,sm_alert_reason => 2},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+check_imei_req_test() ->
+ Bin = <<16#30, ?TEST_IMSI_IE,
+ 16#50, 16#09, %% IMEI
+ 16#42, 16#42, 16#42, 16#42, 16#42, 16#42, 16#42, 16#42, 16#42
+ >>,
+ Map = #{imei => <<16#42, 16#42, 16#42, 16#42, 16#42, 16#42, 16#42, 16#42, 16#42>>,imsi => <<"123456789012345">>,
+ message_type => check_imei_req},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+check_imei_err_test() ->
+ Bin = <<16#31, ?TEST_IMSI_IE, 16#02, 16#01, 16#60>>,
+ Map = #{cause => 96,imsi => <<"123456789012345">>,
+ message_type => check_imei_err},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
+check_imei_res_test() ->
+ Bin = <<16#32, ?TEST_IMSI_IE,
+ 16#51, 16#01,
+ 16#00 %% OSMO_GSUP_IMEI_RESULT_ACK
+ >>,
+ Map = #{imei_check_result => 0,imsi => <<"123456789012345">>,
+ message_type => check_imei_res},
+ ?assertEqual(Map, gsup_protocol:decode(Bin)),
+ ?assertEqual(Bin, gsup_protocol:encode(Map)).
+
diff --git a/test/ipa_encode_decode_test.erl b/test/ipa_encode_decode_test.erl
new file mode 100644
index 0000000..b2ad88f
--- /dev/null
+++ b/test/ipa_encode_decode_test.erl
@@ -0,0 +1,25 @@
+% This Source Code Form is subject to the terms of the Mozilla Public
+% License, v. 2.0. If a copy of the MPL was not distributed with this
+% file, You can obtain one at https://mozilla.org/MPL/2.0/.
+% (C) 2019 Andrey Velikiy <agreat22@gmail.com>
+% (C) 2019 Fairwaves (edited)
+
+-module (ipa_encode_decode_test).
+
+-include_lib("eunit/include/eunit.hrl").
+
+ping_test() ->
+ ?assertEqual({reply, ping, <<00,01,16#fe,01>>,<<>>}, ipa:decode(<<00,01,16#fe,00>>)),
+ ?assertEqual({reply, ack, <<00,01,16#fe,04>>,<<>>}, ipa:decode(<<00,01,16#fe,06>>)),
+ ?assertEqual({reply, resp, <<00,01,16#fe,06>>,<<>>}, ipa:decode(<<00,01,16#fe,05>>)).
+
+more_data_test() ->
+ ?assertEqual({more_data, <<00,01,16#fe>>}, ipa:decode(<<00,01,16#fe>>)),
+ ?assertEqual({more_data, <<00,06,16#ee,5,1,2>>}, ipa:decode(<<00,06,16#ee,5,1,2>>)).
+
+error_test() ->
+ ?assertEqual({error, {bad_stream_id, 255}}, ipa:decode(<<00,01,16#ff,1>>)),
+ ?assertEqual({error, {bad_protocol_extension, 1}}, ipa:decode(<<00,01,16#ee,1>>)).
+
+ok_test() ->
+ ?assertEqual({ok,{<<1,2,3,4,5>>, <<6,7>>}}, ipa:decode(<<00,06,16#ee,5,1,2,3,4,5,6,7>>)).