diff options
-rw-r--r-- | lib/diameter/test/diameter_3xxx_SUITE.erl | 233 |
1 files changed, 203 insertions, 30 deletions
diff --git a/lib/diameter/test/diameter_3xxx_SUITE.erl b/lib/diameter/test/diameter_3xxx_SUITE.erl index a87d5347db..c780fd9859 100644 --- a/lib/diameter/test/diameter_3xxx_SUITE.erl +++ b/lib/diameter/test/diameter_3xxx_SUITE.erl @@ -18,7 +18,9 @@ %% %% -%% Tests of application_opt() request_errors. +%% Tests of application_opt() request_errors. There's some overlap +%% between this suite and the traffic suite but latter exercises more +%% config. %% -module(diameter_3xxx_SUITE). @@ -35,16 +37,19 @@ %% testcases -export([start/1, - send/1, + send_unknown_command/1, + send_ok_override/1, + send_invalid_avp_bits/1, + send_double_error/1, stop/1]). %% diameter callbacks -export([peer_up/3, peer_down/3, - pick_peer/4, - prepare_request/3, - handle_answer/4, - handle_error/4, + pick_peer/5, + prepare_request/4, + handle_answer/5, + handle_error/5, handle_request/3]). -include("diameter.hrl"). @@ -73,13 +78,22 @@ {answer_errors, callback}, {request_errors, RequestErrors}]}]). --define(SUCCESS, ?'DIAMETER_BASE_RESULT-CODE_SUCCESS'). --define(UNSUPPORTED, ?'DIAMETER_BASE_RESULT-CODE_COMMAND_UNSUPPORTED'). +-define(SUCCESS, 2001). +-define(UNSUPPORTED_COMMAND, 3001). +-define(INVALID_AVP_BITS, 3009). +-define(UNKNOWN_SESSION_ID, 5002). +-define(MISSING_AVP, 5005). -define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT'). -define(GROUPS, [answer_3xxx, callback]). -define(L, atom_to_list). +-define(A, list_to_atom). +-define(v, proplists:get_value). + +-define(testcase(Config), put({?MODULE, testcase}, ?v(testcase, Config))). +-define(testcase(), get({?MODULE, testcase})). +-define(group(Config), ?v(group, Config)). %% =========================================================================== @@ -90,7 +104,8 @@ all() -> [{group, G} || G <- ?GROUPS]. groups() -> - [{G, [], [start, send, stop]} || G <- ?GROUPS]. + Tc = tc(), + [{G, [], [start] ++ Tc ++ [stop]} || G <- ?GROUPS]. init_per_suite(Config) -> ok = diameter:start(), @@ -105,8 +120,8 @@ init_per_group(Group, Config) -> end_per_group(_, _) -> ok. -init_per_testcase(_Name, Config) -> - Config. +init_per_testcase(Name, Config) -> + [{testcase, Name} | Config]. end_per_testcase(_, _) -> ok. @@ -117,6 +132,12 @@ origin(callback) -> 1; origin(0) -> answer_3xxx; origin(1) -> callback. +tc() -> + [send_unknown_command, + send_ok_override, + send_invalid_avp_bits, + send_double_error]. + %% =========================================================================== %% start/1 @@ -139,30 +160,90 @@ stop(_Config) -> ok = diameter:stop_service(?SERVER), ok = diameter:stop_service(?CLIENT). -%% send/1 +%% send_unknown_command/1 %% %% Send a unknown command and expect a different result depending on %% whether or not the server gets a handle_request callback. %% Server handle_request discards the request. -send(callback) -> +send_unknown_command(callback) -> {error, timeout} = call(); %% No handle_request, diameter answers. -send(answer_3xxx) -> - #'diameter_base_answer-message'{'Result-Code' = ?UNSUPPORTED} = call(); +send_unknown_command(answer_3xxx) -> + #'diameter_base_answer-message'{'Result-Code' = ?UNSUPPORTED_COMMAND} + = call(); + +send_unknown_command(Config) -> + ?testcase(Config), + send_unknown_command(?group(Config)). -send(Config) -> - send(proplists:get_value(group, Config)). +%% send_ok_override/1 +%% +%% Send a correct STA and expect the same answer from handle_request +%% in both cases. + +send_ok_override(A) + when is_atom(A) -> + #diameter_base_STA{'Result-Code' = ?UNKNOWN_SESSION_ID} + = call(); + +send_ok_override(Config) -> + ?testcase(Config), + send_ok_override(?group(Config)). + +%% send_invalid_avp_bits/1 +%% +%% Send a request with an incorrect length on the optional +%% Origin-State-Id and expect a callback to ignore the error. + +send_invalid_avp_bits(callback) -> + #diameter_base_STA{'Result-Code' = ?SUCCESS, + 'Failed-AVP' = []} + = call(); + +send_invalid_avp_bits(answer_3xxx) -> + #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS, + 'Failed-AVP' = []} + = call(); + +send_invalid_avp_bits(Config) -> + ?testcase(Config), + send_invalid_avp_bits(?group(Config)). + +%% send_double_error/1 +%% +%% Send a request with both an incorrect length on the optional +%% Origin-State-Id and a missing AVP and see that it's answered +%% differently. + +%% diameter answers with the 3xxx error. +send_double_error(answer_3xxx) -> + #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS, + 'Failed-AVP' = [_]} + = call(); + +%% handle_request answers with STA and diameter resets Result-Code. +send_double_error(callback) -> + #diameter_base_STA{'Result-Code' = ?MISSING_AVP, + 'Failed-AVP' = [_]} + = call(); + +send_double_error(Config) -> + ?testcase(Config), + send_double_error(?group(Config)). %% =========================================================================== call() -> + Name = ?testcase(), diameter:call(?CLIENT, ?DICT, #diameter_base_STR {'Termination-Cause' = ?LOGOUT, - 'Auth-Application-Id' = ?DIAMETER_APP_ID_COMMON}). + 'Auth-Application-Id' = ?DIAMETER_APP_ID_COMMON, + 'Class' = [?L(Name)]}, + [{extra, [Name]}]). %% =========================================================================== %% diameter callbacks @@ -177,14 +258,19 @@ peer_up(_SvcName, _Peer, State) -> peer_down(_SvcName, _Peer, State) -> State. -%% pick_peer/4 +%% pick_peer/5 -pick_peer([Peer], _, ?CLIENT, _State) -> +pick_peer([Peer], _, ?CLIENT, _State, _Name) -> {ok, Peer}. -%% prepare_request/3 +%% prepare_request/4 + +prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) -> + {send, prepare(Pkt, Caps, Name)}. -prepare_request(#diameter_packet{msg = Req0} = Pkt0, ?CLIENT, {_Ref, Caps}) -> +prepare(Pkt0, Caps, send_unknown_command) -> + #diameter_packet{msg = Req0} + = Pkt0, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, DR}} = Caps, @@ -196,19 +282,106 @@ prepare_request(#diameter_packet{msg = Req0} = Pkt0, ?CLIENT, {_Ref, Caps}) -> = Pkt = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}), - {send, Pkt#diameter_packet{bin = <<H/binary, 572:24, T/binary>>}}. + Pkt#diameter_packet{bin = <<H/binary, 572:24, T/binary>>}; -%% handle_answer/4 - -handle_answer(Pkt, _Req, ?CLIENT, _Peer) -> +prepare(Pkt, Caps, send_ok_override) -> + #diameter_packet{msg = Req} + = Pkt, + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, DR}} + = Caps, + Req#diameter_base_STR{'Session-Id' = diameter:session_id(OH), + 'Origin-Host' = OH, + 'Origin-Realm' = OR, + 'Destination-Realm' = DR}; + +prepare(Pkt, Caps, send_invalid_avp_bits) -> + #diameter_packet{msg = Req0} + = Pkt, + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, DR}} + = Caps, + %% Append an Origin-State-Id with an incorrect AVP Length in order + %% to force 3009. + Req = Req0#diameter_base_STR{'Session-Id' = diameter:session_id(OH), + 'Origin-Host' = OH, + 'Origin-Realm' = OR, + 'Destination-Realm' = DR, + 'Origin-State-Id' = [7]}, + #diameter_packet{bin = Bin} + = diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}), + Offset = size(Bin) - 12 + 5, + <<H:Offset/binary, Len:24, T/binary>> = Bin, + Pkt#diameter_packet{bin = <<H/binary, (Len + 2):24, T/binary>>}; + +prepare(Pkt0, Caps, send_double_error) -> + #diameter_packet{bin = Bin} + = Pkt + = prepare(Pkt0, Caps, send_invalid_avp_bits), + %% Keep Session-Id but remove Origin-Host. + <<V, Len:24, H:16/binary, T0/binary>> + = Bin, + {SessionId, T1} = split_avp(T0), + {OriginHost, T} = split_avp(T1), + Delta = size(OriginHost), + Pkt#diameter_packet{bin = <<V, (Len - Delta):24, H/binary, + SessionId/binary, + T/binary>>}. + +%% handle_answer/5 + +handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Name) -> Pkt#diameter_packet.msg. -%% handle_error/4 +%% handle_error/5 -handle_error(Reason, _Req, ?CLIENT, _Peer) -> +handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) -> {error, Reason}. +split_avp(<<_:5/binary, Len:24, _/binary>> = Bin) -> + L = pad(Len), + <<Avp:L/binary, T/binary>> = Bin, + {Avp, T}. + +pad(N) + when 0 == N rem 4 -> + N; +pad(N) -> + N - (N rem 4) + 4. + %% handle_request/3 -handle_request(_, ?SERVER, _) -> - discard. +%% send_unknown_command +handle_request(#diameter_packet{msg = undefined}, ?SERVER, _) -> + discard; + +handle_request(#diameter_packet{msg = Req}, ?SERVER, {_, Caps}) -> + #diameter_base_STR{'Class' = [Name]} + = Req, + {reply, request(?A(Name), Req, Caps)}. + +request(send_ok_override, Req, Caps) -> + #diameter_packet{msg = answer(Req, Caps), + errors = [?UNKNOWN_SESSION_ID]}; %% override + +request(send_invalid_avp_bits, Req, Caps) -> + #diameter_base_STR{'Origin-State-Id' = []} + = Req, + %% Default errors field but a non-answer-message and only 3xxx + %% errors detected means diameter sets neither Result-Code nor + %% Failed-AVP. + #diameter_packet{msg = answer(Req, Caps)}; + +request(send_double_error, Req, Caps) -> + answer(Req, Caps). + +answer(Req, Caps) -> + #diameter_base_STR{'Session-Id' = SId} + = Req, + #diameter_caps{origin_host = {OH,_}, + origin_realm = {OR,_}} + = Caps, + #diameter_base_STA{'Session-Id' = SId, + 'Origin-Host' = OH, + 'Origin-Realm' = OR, + 'Result-Code' = ?SUCCESS}. |