Bug #6399
closedosmo-epdg: S6b ASR encoding fails
100%
Description
I couldn't figure out yet why encoding the following message fails when running test EPDG_Tests.TC_hss_initiated_deregister_permanent_termination:
18:15:53.642 [debug] S6b Tx ASR: {'ASR',["aaa.localdomain",";","1771858795",";","30",";","nonode@nohost"],undefined,undefined,undefined,undefined,16777272,"262422638508077",1,[]} 18:15:53.642 [debug] S6b prepare_request: {'ASR',["aaa.localdomain",";","1771858795",";","30",";","nonode@nohost"],"aaa.localdomain","localdomain","localdomain","pgw.localdomain",16777272,"262422638508077",1,[]} 18:15:53.643 [error] Error: encode
The procedure is introduced here: https://gerrit.osmocom.org/c/erlang/osmo-epdg/+/36262
Grep for "handle_call({asr, Imsi}, _From, State)" in src/aaa_diameter_s6b.erl.
Updated by pespin about 2 months ago
- Status changed from Feedback to Resolved
- % Done changed from 0 to 100
I added a test function reproducing the issue:
+foobar() -> + State = #s6b_state{tx_timeout = 10000}, + Imsi = "262422638508077", + SessionId = diameter:session_id(application:get_env(?ENV_APP_NAME, dia_s6b_origin_host, ?ENV_DEFAULT_ORIG_HOST)), + ASR = #'ASR'{'Session-Id' = SessionId, + 'Auth-Application-Id' = ?DIAMETER_APP_ID_S6b, + 'User-Name' = Imsi, + 'Auth-Session-State' = ?'AUTH-SESSION-STATE_NO_STATE_MAINTAINED' + }, + lager:debug("S6b Tx ASR: ~p~n", [ASR]), + Ret = diameter_call(ASR, State), + ok = Ret.
I traced the problem by using the following commands in the shell:
erlang:trace(all, true, [call]). erlang:trace_pattern({aaa_diameter_s6b, 'foobar', '_'}, true, [local]). erlang:trace_pattern({aaa_diameter_s6b, '_', '_'}, [{'_', [], [{return_trace}]}], [local]). erlang:trace_pattern({diameter, '_', '_'}, true, [local]). erlang:trace_pattern({diameter, '_', '_'}, [{'_', [], [{return_trace}]}], [local]). erlang:trace_pattern({diameter_codec, '_', '_'}, true, [local]). erlang:trace_pattern({diameter_codec, '_', '_'}, [{'_', [], [{return_trace}]}], [local]). erlang:trace_pattern({diameter_traffic, '_', '_'}, true, [local]). erlang:trace_pattern({diameter_traffic, '_', '_'}, [{'_', [], [{return_trace}]}], [local]). erlang:trace_pattern({diameter_types, 'UTF8String', '_'}, true, [local]). erlang:trace_pattern({diameter_types, 'UTF8String', '_'}, [{'_', [], [{return_trace}]}], [local]). erlang:trace_pattern({diameter_gen, 'enc1', '_'}, true, [local]). erlang:trace_pattern({diameter_gen, 'enc1', '_'}, [{'_', [], [{return_trace}]}], [local]). spawn(fun() -> aaa_diameter_s6b:foobar() end).
Then I use "flush()." to see the output of the trace.
Interesting parts here:
Shell got {trace,<0.546.0>,call, {diameter_gen,enc1, ['User-Name', {1,64,undefined}, 50, #{decode_format => record, incoming_maxlen => 16777215, module => diameter_3gpp_ts29_273_s6b, restrict_connections => nodes, sequence => {0,32}, share_peers => false,spawn_opt => [], strict_mbit => true,string_decode => true, traffic_counters => true, use_shared_peers => false}, diameter_3gpp_ts29_273_s6b]}} Shell got {trace,<0.546.0>,call, {diameter_types,'UTF8String', [encode,50, #{decode_format => record, incoming_maxlen => 16777215, module => diameter_gen_base_rfc6733, restrict_connections => nodes, sequence => {0,32}, share_peers => false,spawn_opt => [], strict_mbit => true,string_decode => true, traffic_counters => true, use_shared_peers => false}]}} Shell got {trace,<0.546.0>,call, {diameter_traffic,incr_error, [send, {{encode_failure,badarg,'ASR', [{unicode,characters_to_binary,"2", [{file,"unicode.erl"}, {line,895}, {error_info,#{module => erl_stdlib_errors}}]}, {diameter_types,'UTF8String',3, [{file,"base/diameter_types.erl"},{line,363}]}, {diameter_gen,enc1,5, [{file,"base/diameter_gen.erl"},{line,189}]}, {diameter_gen,enc,9, [{file,"base/diameter_gen.erl"},{line,167}]}, {diameter_gen,'-encode/5-lc$^3/1-3-',5, [{file,"base/diameter_gen.erl"},{line,106}]}, {diameter_gen,'-encode/5-lc$^3/1-3-',5, [{file,"base/diameter_gen.erl"},{line,106}]}, {diameter_gen,encode_avps,3, [{file,"base/diameter_gen.erl"},{line,71}]}, {diameter_codec,enc,3, [{file,"base/diameter_codec.erl"},{line,161}]}]}, [{diameter_gen,encode_avps,3, [{file,"base/diameter_gen.erl"},{line,84}]}, {diameter_codec,enc,3, [{file,"base/diameter_codec.erl"},{line,161}]}, {diameter_codec,encode,3, [{file,"base/diameter_codec.erl"},{line,88}]}, {diameter_traffic,encode,4, [{file,"base/diameter_traffic.erl"},{line,1823}]}, {diameter_traffic,send_request,6, [{file,"base/diameter_traffic.erl"},{line,1459}]}, {diameter_traffic,'-send_request/4-fun-0-',6, [{file,"base/diameter_traffic.erl"},{line,1342}]}], {diameter_header,1,undefined,274,16777272,3213723902, 3213723902,true,true,false,false}}, <0.508.0>,diameter_3gpp_ts29_273_s6b]}}
Fixed with:
diff --git a/src/aaa_diameter_s6b.erl b/src/aaa_diameter_s6b.erl index c9afc37..94b4aea 100644 --- a/src/aaa_diameter_s6b.erl +++ b/src/aaa_diameter_s6b.erl @@ -146,8 +148,8 @@ handle_call({asr, Imsi}, _From, State) -> SessionId = diameter:session_id(application:get_env(?ENV_APP_NAME, dia_s6b_origin_host, ?ENV_DEFAULT_ORIG_HOST)), ASR = #'ASR'{'Session-Id' = SessionId, 'Auth-Application-Id' = ?DIAMETER_APP_ID_S6b, - 'User-Name' = Imsi, - 'Auth-Session-State' = ?'AUTH-SESSION-STATE_NO_STATE_MAINTAINED' + 'User-Name' = [Imsi], + 'Auth-Session-State' = [?'AUTH-SESSION-STATE_NO_STATE_MAINTAINED']
So it seems if I don't pass Imsi inside the "optional" array, then since Imsi itself is an aray of characters, the string is discarded and the first character taken instead of the full string. Then "diameter_types:'UTF8String'()" fails to convert an integer/character to binary.