diff options
Diffstat (limited to 'lib/diameter/test/diameter_traffic_SUITE.erl')
-rw-r--r-- | lib/diameter/test/diameter_traffic_SUITE.erl | 447 |
1 files changed, 364 insertions, 83 deletions
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 6eed8d3b5d..b03a9ce4d1 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-2013. 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,10 @@ add_transports/1, result_codes/1, send_ok/1, + send_nok/1, + send_eval/1, + send_bad_answer/1, + send_protocol_error/1, send_arbitrary/1, send_unknown/1, send_unknown_mandatory/1, @@ -45,6 +49,9 @@ send_unsupported_app/1, send_error_bit/1, send_unsupported_version/1, + send_invalid_avp_bits/1, + send_invalid_avp_length/1, + send_invalid_reject/1, send_long/1, send_nopeer/1, send_noapp/1, @@ -80,15 +87,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,13 +109,31 @@ -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 + +%% How to construct messages, as record or list. -define(ENCODINGS, [list, record]). --define(DICT, ?DIAMETER_DICT_COMMON). +%% How to send answers, in a diameter_packet or not. +-define(CONTAINERS, [pkt, msg]). + +%% Send over multiple connections that are mapped onto +%% [{E,P} || E <- ?ENCODINGS, P <- ?CONTAINERS]. +-define(CONNECTIONS, [c0,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). +%% An Application-ID the server doesn't support. +-define(BAD_APP, 42). + %% Config for diameter:start_service/2. -define(SERVICE(Name), [{'Origin-Host', Name ++ "." ++ ?REALM}, @@ -115,11 +141,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 +159,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, @@ -139,6 +169,8 @@ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_REALM_NOT_SERVED'). -define(UNABLE_TO_DELIVER, ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNABLE_TO_DELIVER'). +-define(INVALID_AVP_LENGTH, + ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_LENGTH'). -define(EVENT_RECORD, ?'DIAMETER_BASE_ACCOUNTING-RECORD-TYPE_EVENT_RECORD'). @@ -151,27 +183,35 @@ ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT'). -define(BAD_ANSWER, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_BAD_ANSWER'). +-define(USER_MOVED, + ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED'). -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([R,C,A]), P} || R <- ?ENCODINGS, + C <- ?CONTAINERS, + A <- ?ENCODINGS, + P <- [[], [parallel]]] ++ [remove_transports, stop_services, stop]. groups() -> Ts = tc(), - [{E, [], Ts} || E <- ?ENCODINGS]. + [{?util:name([R,C,A]), [], Ts} || R <- ?ENCODINGS, + C <- ?CONTAINERS, + A <- ?ENCODINGS]. init_per_group(Name, Config) -> - [{encode, Name} | Config]. + [{group, Name} | Config]. end_per_group(_, _) -> ok. @@ -186,6 +226,10 @@ end_per_testcase(_, _) -> %% established. tc() -> [send_ok, + send_nok, + send_eval, + send_bad_answer, + send_protocol_error, send_arbitrary, send_unknown, send_unknown_mandatory, @@ -194,6 +238,9 @@ tc() -> send_unsupported_app, send_error_bit, send_unsupported_version, + send_invalid_avp_bits, + send_invalid_avp_length, + send_invalid_reject, send_long, send_nopeer, send_noapp, @@ -231,16 +278,19 @@ 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}, + {capabilities, [osi(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), @@ -253,11 +303,15 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) -> io:format("connection: ~p -> ~p~n", [DH,OH]), ok. +osi(Id) -> + [$c,N] = atom_to_list(Id), + {'Origin-State-Id', N - $0}. + %% =========================================================================== %% 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, 5014} = {?SUCCESS, ?COMMAND_UNSUPPORTED, ?UNABLE_TO_DELIVER, @@ -265,14 +319,50 @@ result_codes(_Config) -> ?TOO_BUSY, ?APPLICATION_UNSUPPORTED, ?INVALID_HDR_BITS, + ?INVALID_AVP_BITS, ?AVP_UNSUPPORTED, - ?UNSUPPORTED_VERSION}. + ?UNSUPPORTED_VERSION, + ?INVALID_AVP_LENGTH}. %% Send an ACR and expect success. 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 ACR and expect success. +send_eval(Config) -> + Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, + {'Accounting-Record-Number', 3}], + + #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS} + = 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 ACR that the server callback answers explicitly with a +%% protocol error. +send_protocol_error(Config) -> + Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, + {'Accounting-Record-Number', 4}], + + #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY} = call(Config, Req). %% Send an ASR with an arbitrary AVP and expect success and the same @@ -324,7 +414,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} @@ -342,6 +432,29 @@ send_unsupported_version(Config) -> #diameter_base_STA{'Result-Code' = ?UNSUPPORTED_VERSION} = call(Config, Req). +%% Send a request containing an incorrect AVP length. +send_invalid_avp_bits(Config) -> + Req = ['STR', {'Termination-Cause', ?LOGOUT}], + + #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS} + = call(Config, Req). + +%% Send a request containing an AVP length that doesn't match the +%% AVP's type. +send_invalid_avp_length(Config) -> + Req = ['STR', {'Termination-Cause', ?LOGOUT}], + + #'diameter_base_STA'{'Result-Code' = ?INVALID_AVP_LENGTH} + = call(Config, Req). + +%% Send a request containing 5xxx errors that the server rejects with +%% 3xxx. +send_invalid_reject(Config) -> + Req = ['STR', {'Termination-Cause', ?USER_MOVED}], + + #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY} + = call(Config, Req). + %% Send something long that will be fragmented by TCP. send_long(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}, @@ -516,32 +629,67 @@ call(Config, Req) -> call(Config, Req, Opts) -> Name = proplists:get_value(testcase, Config), - Enc = proplists:get_value(encode, Config), + [Encoding, C, E] = ?util:name(proplists:get_value(group, Config)), diameter:call(?CLIENT, - ?APP_ALIAS, - msg(Req, Enc), - [{extra, [Name]} | Opts]). + dict(Req), + msg(Req, Encoding), + [{extra, [Name, client(E,C)]} | Opts]). + +client(E, C) -> + list_to_atom([$c, $0 + 2*codec(E) + container(C)]). + +client(N) -> + {codec(N bsr 1), container(N rem 2)}. + +codec(record) -> 0; +codec(list) -> 1; +codec(0) -> record; +codec(1) -> list. + +%% Here we're just mapping booleans but the readable atoms are part of +%% (constructed) group names, so it's good that they're readable. -msg([_|_] = L, list) -> - L; +container(pkt) -> 0; +container(msg) -> 1; +container(0) -> pkt; +container(1) -> msg. + +msg([H|T], record) + when H == 'ACR'; + H == 'ACA' -> + ?ACCT:'#new-'(?ACCT:msg2rec(H), T); msg([H|T], record) -> - ?DICT:'#new-'(?DICT:msg2rec(H), T); + ?BASE:'#new-'(?BASE:msg2rec(H), T); msg(T, _) -> T. +dict(['ACR' | _]) -> + ?ACCT; +dict(#diameter_base_accounting_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,42 +705,88 @@ 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}. -%% prepare_request/4/5 +id(Id, {Pid, _Caps}) -> + [{ref, _}, {type, _}, {options, Opts} | _] + = diameter:service_info(?CLIENT, Pid), + lists:member({id, Id}, Opts). -prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard) -> +%% prepare_request/5-6 + +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/3 + +prepare(Pkt, Caps, send_invalid_avp_bits) -> + Req = prepare(Pkt, Caps), + %% Last AVP in our STR is Termination-Cause of type Unsigned32: + %% set its length improperly. + #diameter_packet{header = #diameter_header{length = L}, + bin = B} + = E + = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}), + Offset = L - 7, %% to AVP Length + <<H:Offset/binary, 12:24/integer, T:4/binary>> = B, + E#diameter_packet{bin = <<H/binary, 13:24/integer, T/binary>>}; + +prepare(Pkt, Caps, N) + when N == send_invalid_avp_length; + N == send_invalid_reject -> + Req = prepare(Pkt, Caps), + %% Second last AVP in our STR is Auth-Application-Id of type + %% Unsigned32: Send a value of length 8. + #diameter_packet{header = #diameter_header{length = L}, + bin = B0} + = E + = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}), + Offset = L - 7 - 12, %% to AVP Length + <<H0:Offset/binary, 12:24/integer, T:16/binary>> = B0, + <<V, L:24/integer, H/binary>> = H0, %% assert + E#diameter_packet{bin = <<V, + (L+4):24/integer, + H/binary, + 16:24/integer, + 0:32/integer, + T/binary>>}; 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}), - E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>}; + = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}), + E#diameter_packet{bin = <<H/binary, ?BAD_APP:32/integer, T/binary>>}; prepare(Pkt, Caps, send_error_bit) -> #diameter_packet{header = Hdr} = Pkt, @@ -611,8 +805,10 @@ prepare(Pkt, Caps, send_anything) -> prepare(Pkt, Caps, _Name) -> prepare(Pkt, Caps). +%% prepare/2 + 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 +857,42 @@ 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, _) -> +answer(Pkt, Req, _Peer, Name) -> + #diameter_packet{header = H, msg = Rec, errors = Es} = Pkt, + ApplId = app(Req, Name), + #diameter_header{application_id = ApplId} = H, %% assert + answer(Rec, Es, Name). + +answer(Rec, [_|_], N) + when N == send_invalid_avp_bits; + N == send_invalid_avp_length; + N == send_invalid_reject -> + Rec; +answer(Rec, [], _) -> Rec. -%% handle_error/5 +app(_, send_unsupported_app) -> + ?BAD_APP; +app(Req, _) -> + Dict = dict(Req), + Dict:id(). + +%% 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 +900,64 @@ 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). +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, %% + #diameter_caps{origin_state_id = {_,[N]}} = Caps, + answer(client(N), request(M, Caps)). + +answer(T, {Tag, Action, Post}) -> + {Tag, answer(T, Action), Post}; +answer({E,C}, {reply, Ans}) -> + answer(C, {reply, msg(Ans, E)}); +answer(pkt, {reply, Ans}) -> + {reply, #diameter_packet{msg = Ans}}; +answer(_, T) -> + T. -request(#diameter_packet{msg - = #diameter_base_ACR{'Session-Id' = SId, - 'Accounting-Record-Type' = RT, - 'Accounting-Record-Number' = RN}}, +%% send_nok +request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0}, + _) -> + {eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]}; + +%% send_bad_answer +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}}; + +%% send_eval +request(#diameter_base_accounting_ACR{'Session-Id' = SId, + 'Accounting-Record-Type' = RT, + 'Accounting-Record-Number' = 3 = 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}], + {eval, {reply, Ans}, {erlang, now, []}}; + +%% send_ok +request(#diameter_base_accounting_ACR{'Session-Id' = SId, + 'Accounting-Record-Type' = RT, + 'Accounting-Record-Number' = 1 = RN}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ACA', {'Result-Code', ?SUCCESS}, @@ -703,39 +967,56 @@ request(#diameter_packet{msg {'Accounting-Record-Type', RT}, {'Accounting-Record-Number', RN}]}; -request(#diameter_packet{msg = #diameter_base_ASR{'Session-Id' = SId, - 'AVP' = Avps}}, +%% send_protocol_error +request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> - {reply, #diameter_base_ASA{'Result-Code' = ?SUCCESS, - 'Session-Id' = SId, - 'Origin-Host' = OH, - 'Origin-Realm' = OR, - 'AVP' = Avps}}; + Ans = ['answer-message', {'Result-Code', ?TOO_BUSY}, + {'Origin-Host', OH}, + {'Origin-Realm', OR}], + {reply, Ans}; -request(#diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = T}}, +request(#diameter_base_ASR{'Session-Id' = SId, + 'AVP' = Avps}, + #diameter_caps{origin_host = {OH, _}, + origin_realm = {OR, _}}) -> + {reply, ['ASA', {'Result-Code', ?SUCCESS}, + {'Session-Id', SId}, + {'Origin-Host', OH}, + {'Origin-Realm', OR}, + {'AVP', Avps}]}; + +%% send_invalid_reject +request(#diameter_base_STR{'Termination-Cause' = ?USER_MOVED}, _Caps) -> + {protocol_error, ?TOO_BUSY}; + +%% send_noreply +request(#diameter_base_STR{'Termination-Cause' = T}, _Caps) when T /= ?LOGOUT -> discard; -request(#diameter_packet{msg = #diameter_base_STR{'Destination-Realm'= R}}, +%% send_destination_5 +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]}}, +%% send_destination_6 +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, - 'Session-Id' = SId, - 'Origin-Host' = OH, - 'Origin-Realm' = OR}}; + {reply, ['STA', {'Result-Code', ?SUCCESS}, + {'Session-Id', SId}, + {'Origin-Host', OH}, + {'Origin-Realm', OR}]}; -request(#diameter_packet{msg = #diameter_base_RAR{}}, _Caps) -> +%% send_error +request(#diameter_base_RAR{}, _Caps) -> receive after 2000 -> ok end, {protocol_error, ?TOO_BUSY}. |