diff options
Diffstat (limited to 'lib/diameter/test/diameter_traffic_SUITE.erl')
-rw-r--r-- | lib/diameter/test/diameter_traffic_SUITE.erl | 241 |
1 files changed, 166 insertions, 75 deletions
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 6eed8d3b5d..c157b0e304 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,6 +37,8 @@ add_transports/1, result_codes/1, send_ok/1, + send_nok/1, + send_bad_answer/1, send_arbitrary/1, send_unknown/1, send_unknown_mandatory/1, @@ -80,15 +82,16 @@ %% diameter callbacks -export([peer_up/3, peer_down/3, - pick_peer/5, pick_peer/6, - prepare_request/4, prepare_request/5, - prepare_retransmit/4, - handle_answer/5, handle_answer/6, - handle_error/5, + pick_peer/6, pick_peer/7, + prepare_request/5, prepare_request/6, + prepare_retransmit/5, + handle_answer/6, handle_answer/7, + handle_error/6, handle_request/3]). -include("diameter.hrl"). -include("diameter_gen_base_rfc3588.hrl"). +-include("diameter_gen_base_accounting.hrl"). %% =========================================================================== @@ -101,11 +104,23 @@ -define(REALM, "erlang.org"). -define(HOST(Host, Realm), Host ++ [$.|Realm]). --define(APP_ALIAS, base). -define(EXTRA, an_extra_argument). + +-define(BASE, ?DIAMETER_DICT_COMMON). +-define(ACCT, ?DIAMETER_DICT_ACCOUNTING). + +%% Sequence mask for End-to-End and Hop-by-Hop identifiers. +-define(CLIENT_MASK, {1,26}). %% 1 in top 6 bits + +%% Run tests cases in different encoding variants. Send outgoing +%% messages as lists or records. -define(ENCODINGS, [list, record]). --define(DICT, ?DIAMETER_DICT_COMMON). +%% Identifers for client connections. +-define(CONNECTIONS, [c1,c2,c3]). + +%% Not really what we should be setting unless the message is sent in +%% the common application but diameter doesn't care. -define(APP_ID, ?DIAMETER_APP_ID_COMMON). %% Config for diameter:start_service/2. @@ -115,11 +130,13 @@ {'Host-IP-Address', [?ADDR]}, {'Vendor-Id', 12345}, {'Product-Name', "OTP/diameter"}, - {'Acct-Application-Id', [?DIAMETER_APP_ID_COMMON]}, - {application, [{alias, ?APP_ALIAS}, - {dictionary, ?DIAMETER_DICT_COMMON}, - {module, ?MODULE}, - {answer_errors, callback}]}]). + {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]}, + {'Acct-Application-Id', [?DIAMETER_APP_ID_ACCOUNTING]}, + {restrict_connections, false} + | [{application, [{dictionary, D}, + {module, ?MODULE}, + {answer_errors, callback}]} + || D <- [?BASE, ?ACCT]]]). -define(SUCCESS, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS'). @@ -131,6 +148,8 @@ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_APPLICATION_UNSUPPORTED'). -define(INVALID_HDR_BITS, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_HDR_BITS'). +-define(INVALID_AVP_BITS, + ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_BITS'). -define(AVP_UNSUPPORTED, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_AVP_UNSUPPORTED'). -define(UNSUPPORTED_VERSION, @@ -154,24 +173,27 @@ -define(A, list_to_atom). -define(L, atom_to_list). --define(P(N), ?A("p_" ++ ?L(N))). + +-define(NAME(A,B), ?A(?L(A) ++ "," ++ ?L(B))). %% =========================================================================== suite() -> - [{timetrap, {seconds, 10}}]. + [{timetrap, {seconds, 60}}]. all() -> [start, start_services, add_transports, result_codes] - ++ [{group, E, P} || E <- ?ENCODINGS, P <- [[], [parallel]]] + ++ [{group, ?util:name([E,C]), P} || E <- ?ENCODINGS, + C <- ?CONNECTIONS, + P <- [[], [parallel]]] ++ [remove_transports, stop_services, stop]. groups() -> Ts = tc(), - [{E, [], Ts} || E <- ?ENCODINGS]. + [{?util:name([E,C]), [], Ts} || E <- ?ENCODINGS, C <- ?CONNECTIONS]. init_per_group(Name, Config) -> - [{encode, Name} | Config]. + [{group, Name} | Config]. end_per_group(_, _) -> ok. @@ -186,6 +208,8 @@ end_per_testcase(_, _) -> %% established. tc() -> [send_ok, + send_nok, + send_bad_answer, send_arbitrary, send_unknown, send_unknown_mandatory, @@ -231,16 +255,17 @@ start(_Config) -> start_services(_Config) -> ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER)), - ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)). + ok = diameter:start_service(?CLIENT, [{sequence, ?CLIENT_MASK} + | ?SERVICE(?CLIENT)]). add_transports(Config) -> LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx/2}]), - CRef = ?util:connect(?CLIENT, tcp, LRef), - ?util:write_priv(Config, "transport", {LRef, CRef}). + Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C}]) || C <- ?CONNECTIONS], + ?util:write_priv(Config, "transport", [LRef | Cs]). remove_transports(Config) -> - {LRef, CRef} = ?util:read_priv(Config, "transport"), - ?util:disconnect(?CLIENT, CRef, ?SERVER, LRef). + [LRef | Cs] = ?util:read_priv(Config, "transport"), + [?util:disconnect(?CLIENT, C, ?SERVER, LRef) || C <- Cs]. stop_services(_Config) -> ok = diameter:stop_service(?CLIENT), @@ -257,7 +282,7 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) -> %% Ensure that result codes have the expected values. result_codes(_Config) -> - {2001, 3001, 3002, 3003, 3004, 3007, 3008, 5001, 5011} + {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011} = {?SUCCESS, ?COMMAND_UNSUPPORTED, ?UNABLE_TO_DELIVER, @@ -265,6 +290,7 @@ result_codes(_Config) -> ?TOO_BUSY, ?APPLICATION_UNSUPPORTED, ?INVALID_HDR_BITS, + ?INVALID_AVP_BITS, ?AVP_UNSUPPORTED, ?UNSUPPORTED_VERSION}. @@ -272,9 +298,26 @@ result_codes(_Config) -> send_ok(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 1}], - #diameter_base_ACA{'Result-Code' = ?SUCCESS} + + #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS} = call(Config, Req). +%% Send an accounting ACR that the server answers badly to. +send_nok(Config) -> + Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, + {'Accounting-Record-Number', 0}], + + #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS} + = call(Config, Req). + +%% Send an accounting ACR that the server tries to answer with an +%% inappropriate header, resulting in no answer being sent and the +%% request timing out. +send_bad_answer(Config) -> + Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, + {'Accounting-Record-Number', 2}], + {error, timeout} = call(Config, Req). + %% Send an ASR with an arbitrary AVP and expect success and the same %% AVP in the reply. send_arbitrary(Config) -> @@ -324,7 +367,7 @@ send_unsupported(Config) -> #'diameter_base_answer-message'{'Result-Code' = ?COMMAND_UNSUPPORTED} = call(Config, Req). -%% Send an unsupported command and expect 3007. +%% Send an unsupported application and expect 3007. send_unsupported_app(Config) -> Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}], #'diameter_base_answer-message'{'Result-Code' = ?APPLICATION_UNSUPPORTED} @@ -516,32 +559,44 @@ call(Config, Req) -> call(Config, Req, Opts) -> Name = proplists:get_value(testcase, Config), - Enc = proplists:get_value(encode, Config), + [Encoding, Client] = ?util:name(proplists:get_value(group, Config)), diameter:call(?CLIENT, - ?APP_ALIAS, - msg(Req, Enc), - [{extra, [Name]} | Opts]). - -msg([_|_] = L, list) -> - L; -msg([H|T], record) -> - ?DICT:'#new-'(?DICT:msg2rec(H), T); -msg(T, _) -> + dict(Req), + req(Req, Encoding), + [{extra, [Name, Client]} | Opts]). + +req(['ACR' = H | T], record) -> + ?ACCT:'#new-'(?ACCT:msg2rec(H), T); +req([H|T], record) -> + ?BASE:'#new-'(?BASE:msg2rec(H), T); +req(T, _) -> T. +dict(['ACR' | _]) -> + ?ACCT; +dict(_) -> + ?BASE. + %% Set only values that aren't already. set([H|T], Vs) -> [H | Vs ++ T]; +set(#diameter_base_accounting_ACR{} = Rec, Vs) -> + set(Rec, Vs, ?ACCT); set(Rec, Vs) -> - lists:foldl(fun({F,_} = FV, A) -> set(?DICT:'#get-'(F, A), FV, A) end, + set(Rec, Vs, ?BASE). + +set(Rec, Vs, Dict) -> + lists:foldl(fun({F,_} = FV, A) -> + set(Dict, Dict:'#get-'(F, A), FV, A) + end, Rec, Vs). -set(E, FV, Rec) +set(Dict, E, FV, Rec) when E == undefined; E == [] -> - ?DICT:'#set-'(FV, Rec); -set(_, _, Rec) -> + Dict:'#set-'(FV, Rec); +set(_, _, _, Rec) -> Rec. %% =========================================================================== @@ -557,41 +612,53 @@ peer_up(_SvcName, _Peer, State) -> peer_down(_SvcName, _Peer, State) -> State. -%% pick_peer/5/6 +%% pick_peer/6-7 -pick_peer([Peer], _, ?CLIENT, _State, Name) +pick_peer(Peers, _, ?CLIENT, _State, Name, Id) when Name /= send_detach -> - {ok, Peer}. + find(Id, Peers). -pick_peer([_Peer], _, ?CLIENT, _State, send_nopeer, ?EXTRA) -> +pick_peer(_Peers, _, ?CLIENT, _State, send_nopeer, _, ?EXTRA) -> false; -pick_peer([Peer], _, ?CLIENT, _State, send_detach, {_,_}) -> - {ok, Peer}. +pick_peer(Peers, _, ?CLIENT, _State, send_detach, Id, {_,_}) -> + find(Id, Peers). + +find(Id, Peers) -> + [P] = [P || P <- Peers, id(Id, P)], + {ok, P}. + +id(Id, {Pid, _Caps}) -> + [{ref, _}, {type, _}, {options, Opts} | _] + = diameter:service_info(?CLIENT, Pid), + lists:member({id, Id}, Opts). -%% prepare_request/4/5 +%% prepare_request/5-6 -prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard) -> +prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard, _) -> {discard, unprepared}; -prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) -> +prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name, _) -> {send, prepare(Pkt, Caps, Name)}. -prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _) -> - {send, prepare(Pkt, Caps)}. +prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _, _) -> + {eval_packet, {send, prepare(Pkt, Caps)}, [fun log/2, detach]}. + +log(#diameter_packet{} = P, T) -> + io:format("~p: ~p~n", [T,P]). prepare(Pkt, Caps, send_unsupported) -> Req = prepare(Pkt, Caps), #diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>} = E - = diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}), + = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}), E#diameter_packet{bin = <<H/binary, 42:24/integer, T/binary>>}; prepare(Pkt, Caps, send_unsupported_app) -> Req = prepare(Pkt, Caps), #diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>} = E - = diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}), + = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}), E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>}; prepare(Pkt, Caps, send_error_bit) -> @@ -612,7 +679,7 @@ prepare(Pkt, Caps, _Name) -> prepare(Pkt, Caps). prepare(#diameter_packet{msg = Req}, Caps) - when is_record(Req, diameter_base_ACR); + when is_record(Req, diameter_base_accounting_ACR); 'ACR' == hd(Req) -> #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, DR}} @@ -661,25 +728,25 @@ prepare(#diameter_packet{msg = Req}, Caps) {'Destination-Realm', DR}, {'Auth-Application-Id', ?APP_ID}]). -%% prepare_retransmit/4 +%% prepare_retransmit/5 -prepare_retransmit(_Pkt, false, _Peer, _Name) -> +prepare_retransmit(_Pkt, false, _Peer, _Name, _Id) -> discard. -%% handle_answer/5/6 +%% handle_answer/6-7 -handle_answer(Pkt, Req, ?CLIENT, Peer, Name) -> +handle_answer(Pkt, Req, ?CLIENT, Peer, Name, _Id) -> answer(Pkt, Req, Peer, Name). -handle_answer(Pkt, _Req, ?CLIENT, _Peer, send_detach, {Pid, Ref}) -> +handle_answer(Pkt, _Req, ?CLIENT, _Peer, send_detach, _Id, {Pid, Ref}) -> Pid ! {Ref, Pkt}. answer(#diameter_packet{msg = Rec, errors = []}, _Req, _Peer, _) -> Rec. -%% handle_error/5 +%% handle_error/6 -handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) -> +handle_error(Reason, _Req, ?CLIENT, _Peer, _Name, _Id) -> {error, Reason}. %% handle_request/3 @@ -687,13 +754,37 @@ handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) -> %% Note that diameter will set Result-Code and Failed-AVPs if %% #diameter_packet.errors is non-null. -handle_request(Pkt, ?SERVER, {_Ref, Caps}) -> - request(Pkt, Caps). - -request(#diameter_packet{msg - = #diameter_base_ACR{'Session-Id' = SId, - 'Accounting-Record-Type' = RT, - 'Accounting-Record-Number' = RN}}, +handle_request(#diameter_packet{header = H, msg = M}, ?SERVER, {_Ref, Caps}) -> + #diameter_header{end_to_end_id = EI, + hop_by_hop_id = HI} + = H, + {V,B} = ?CLIENT_MASK, + V = EI bsr B, %% assert + V = HI bsr B, %% + request(M, Caps). + +request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0}, + _) -> + {eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]}; + +request(#diameter_base_accounting_ACR{'Session-Id' = SId, + 'Accounting-Record-Type' = RT, + 'Accounting-Record-Number' = 2 = RN}, + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, _}}) -> + Ans = ['ACA', {'Result-Code', ?SUCCESS}, + {'Session-Id', SId}, + {'Origin-Host', OH}, + {'Origin-Realm', OR}, + {'Accounting-Record-Type', RT}, + {'Accounting-Record-Number', RN}], + + {reply, #diameter_packet{header = #diameter_header{is_error = true},%% not + msg = Ans}}; + +request(#diameter_base_accounting_ACR{'Session-Id' = SId, + 'Accounting-Record-Type' = RT, + 'Accounting-Record-Number' = RN}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ACA', {'Result-Code', ?SUCCESS}, @@ -703,8 +794,8 @@ request(#diameter_packet{msg {'Accounting-Record-Type', RT}, {'Accounting-Record-Number', RN}]}; -request(#diameter_packet{msg = #diameter_base_ASR{'Session-Id' = SId, - 'AVP' = Avps}}, +request(#diameter_base_ASR{'Session-Id' = SId, + 'AVP' = Avps}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, #diameter_base_ASA{'Result-Code' = ?SUCCESS, @@ -713,22 +804,22 @@ request(#diameter_packet{msg = #diameter_base_ASR{'Session-Id' = SId, 'Origin-Realm' = OR, 'AVP' = Avps}}; -request(#diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = T}}, +request(#diameter_base_STR{'Termination-Cause' = T}, _Caps) when T /= ?LOGOUT -> discard; -request(#diameter_packet{msg = #diameter_base_STR{'Destination-Realm'= R}}, +request(#diameter_base_STR{'Destination-Realm'= R}, #diameter_caps{origin_realm = {OR, _}}) when R /= undefined, R /= OR -> {protocol_error, ?REALM_NOT_SERVED}; -request(#diameter_packet{msg = #diameter_base_STR{'Destination-Host'= [H]}}, +request(#diameter_base_STR{'Destination-Host'= [H]}, #diameter_caps{origin_host = {OH, _}}) when H /= OH -> {protocol_error, ?UNABLE_TO_DELIVER}; -request(#diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId}}, +request(#diameter_base_STR{'Session-Id' = SId}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, #diameter_base_STA{'Result-Code' = ?SUCCESS, @@ -736,6 +827,6 @@ request(#diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId}}, 'Origin-Host' = OH, 'Origin-Realm' = OR}}; -request(#diameter_packet{msg = #diameter_base_RAR{}}, _Caps) -> +request(#diameter_base_RAR{}, _Caps) -> receive after 2000 -> ok end, {protocol_error, ?TOO_BUSY}. |