aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/test')
-rw-r--r--lib/diameter/test/depend.sed2
-rw-r--r--lib/diameter/test/diameter.cover12
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl130
-rw-r--r--lib/diameter/test/diameter_codec_test.erl6
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_gen_sctp_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_length_SUITE.erl288
-rw-r--r--lib/diameter/test/diameter_stats_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl441
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl16
-rw-r--r--lib/diameter/test/modules.mk5
11 files changed, 655 insertions, 255 deletions
diff --git a/lib/diameter/test/depend.sed b/lib/diameter/test/depend.sed
index 95dca44984..7e0d6e40e5 100644
--- a/lib/diameter/test/depend.sed
+++ b/lib/diameter/test/depend.sed
@@ -38,4 +38,4 @@
s@^-include("@@
s@".*@@
G
-s@^\(.*\)\n\(.*\)@$(EBIN)/\2.$(EMULATOR): \1@
+s@^\(.*\)\n\(.*\)@\2.$(EMULATOR): \1@
diff --git a/lib/diameter/test/diameter.cover b/lib/diameter/test/diameter.cover
index 5fde6c7d01..6586733871 100644
--- a/lib/diameter/test/diameter.cover
+++ b/lib/diameter/test/diameter.cover
@@ -1,6 +1,8 @@
-%% -*- erlang -*-
-{exclude,
- [
- ]
-}.
+{incl_app,diameter,details}.
+{excl_mods, diameter,
+ [diameter_dbg,
+ diameter_info,
+ diameter_etcp,
+ diameter_etcp_sup]
+}.
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index ae128b8203..a4e4195a19 100644
--- a/lib/diameter/test/diameter_capx_SUITE.erl
+++ b/lib/diameter/test/diameter_capx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -27,6 +27,8 @@
-export([suite/0,
all/0,
groups/0,
+ init_per_group/2,
+ end_per_group/2,
init_per_testcase/2,
end_per_testcase/2]).
@@ -53,7 +55,6 @@
peer_down/4]).
-include("diameter.hrl").
--include("diameter_gen_base_rfc3588.hrl").
%% ===========================================================================
@@ -78,8 +79,10 @@
| [{application, [{alias, A},
{dictionary, D},
{module, [?MODULE, A]}]}
- || {A,D} <- [{common, ?DIAMETER_DICT_COMMON},
- {accounting, ?DIAMETER_DICT_ACCOUNTING}]]]).
+ || {A,D} <- [{base3588, diameter_gen_base_rfc3588},
+ {acct3588, diameter_gen_base_accounting},
+ {base6733, diameter_gen_base_rfc6733},
+ {acct6733, diameter_gen_acct_rfc6733}]]]).
-define(A, list_to_atom).
-define(L, atom_to_list).
@@ -88,13 +91,12 @@
-define(caps, #diameter_caps).
-define(packet, #diameter_packet).
--define(cea, #diameter_base_CEA).
--define(answer_message, #'diameter_base_answer-message').
-
-define(fail(T), erlang:error({T, process_info(self(), messages)})).
-define(TIMEOUT, 10000).
+-define(DICTS, [rfc3588, rfc6733]).
+
%% ===========================================================================
suite() ->
@@ -102,15 +104,15 @@ suite() ->
all() -> [start,
start_services,
- add_listeners,
- {group, all},
- {group, all, [parallel]},
- remove_listeners,
+ add_listeners]
+ ++ [{group, D, P} || D <- ?DICTS, P <- [[], [parallel]]]
+ ++ [remove_listeners,
stop_services,
stop].
groups() ->
- [{all, [], lists:flatmap(fun tc/1, tc())}].
+ Tc = lists:flatmap(fun tc/1, tc()),
+ [{D, [], Tc} || D <- ?DICTS].
%% Generate a unique hostname for each testcase so that watchdogs
%% don't prevent a connection from being brought up immediately.
@@ -118,6 +120,12 @@ init_per_testcase(Name, Config) ->
Uniq = ["." ++ integer_to_list(N) || N <- tuple_to_list(now())],
[{host, lists:flatten([?L(Name) | Uniq])} | Config].
+init_per_group(Name, Config) ->
+ [{rfc, Name} | Config].
+
+end_per_group(_, _) ->
+ ok.
+
end_per_testcase(N, _)
when N == start;
N == start_services;
@@ -126,6 +134,7 @@ end_per_testcase(N, _)
N == stop_services;
N == stop ->
ok;
+
end_per_testcase(Name, Config) ->
CRef = ?util:read_priv(Config, Name),
ok = diameter:remove_transport(?CLIENT, CRef).
@@ -155,14 +164,19 @@ start_services(_Config) ->
%% to both this and the common application. Share a common service just
%% to simplify config, and because we can.
add_listeners(Config) ->
- Acct = listen(?SERVER,
- [{capabilities, [{'Origin-Host', ?HOST("acct-srv")},
- {'Auth-Application-Id', []}]},
- {applications, [accounting]},
- {capabilities_cb, [fun server_capx/3, acct]}]),
- Base = listen(?SERVER,
- [{capabilities, [{'Origin-Host', ?HOST("base-srv")}]},
- {capabilities_cb, [fun server_capx/3, base]}]),
+ Acct = [listen(?SERVER,
+ [{capabilities, [{'Origin-Host', ?HOST(H)},
+ {'Auth-Application-Id', []}]},
+ {applications, [A]},
+ {capabilities_cb, [fun server_capx/3, acct]}])
+ || {A,H} <- [{acct3588, "acct3588-srv"},
+ {acct6733, "acct6733-srv"}]],
+ Base = [listen(?SERVER,
+ [{capabilities, [{'Origin-Host', ?HOST(H)}]},
+ {applications, A},
+ {capabilities_cb, [fun server_capx/3, base]}])
+ || {A,H} <- [{[base3588, acct3588], "base3588-srv"},
+ {[base6733, acct6733], "base6733-srv"}]],
?util:write_priv(Config, ?MODULE, {Base, Acct}). %% lref/2 reads
remove_listeners(_Config) ->
@@ -195,8 +209,9 @@ c_no_common_application(Config) ->
client_closed(Config, "acct-srv", fun no_common_application/1, 5010).
no_common_application(Config) ->
+ [Common, _Acct] = apps(Config),
connect(Config, acct, [{capabilities, [{'Acct-Application-Id', []}]},
- {applications, [common]}]).
+ {applications, [Common]}]).
%% ====================
%% Ask the base server to speak accounting with an unknown security
@@ -209,9 +224,10 @@ c_no_common_security(Config) ->
client_closed(Config, "base-srv", fun no_common_security/1, 5017).
no_common_security(Config) ->
+ [Common, _Acct] = apps(Config),
connect(Config, base, [{capabilities, [{'Acct-Application-Id', []},
{'Inband-Security-Id', [17, 18]}]},
- {applications, [common]}]).
+ {applications, [Common]}]).
%% ====================
%% Have the base server reject a decent CER with the protocol error
@@ -221,18 +237,19 @@ s_unknown_peer(Config) ->
server_reject(Config, fun base/1, 3010).
c_unknown_peer(Config) ->
+ Dict0 = dict0(Config),
true = diameter:subscribe(?CLIENT),
- OH = ?HOST("base-srv"),
+ OH = host(Config, "base-srv"),
{CRef, _} = base(Config),
- {'CEA', ?caps{},
- ?packet{msg = ?answer_message{'Origin-Host' = OH,
- 'Result-Code' = 3010}}}
- = client_recv(CRef).
+ {'CEA', ?caps{}, ?packet{msg = Msg}} = client_recv(CRef),
+
+ ['diameter_base_answer-message' | _] = Dict0:'#get-'(Msg),
+ [OH, 3010] = Dict0:'#get-'(['Origin-Host', 'Result-Code'], Msg).
base(Config) ->
- connect(Config, base, []).
+ connect(Config, base, [{applications, apps(Config)}]).
%% ====================
%% Have the base server reject a decent CER with the non-protocol
@@ -266,18 +283,23 @@ s_client_reject(Config) ->
end.
c_client_reject(Config) ->
+ Dict0 = dict0(Config),
true = diameter:subscribe(?CLIENT),
- OH = ?HOST("acct-srv"),
+ OH = host(Config, "acct-srv"),
{CRef, _} = client_reject(Config),
{'CEA', {capabilities_cb, _, discard},
?caps{origin_host = {_, OH}},
- ?packet{msg = ?cea{'Result-Code' = 2001}}}
- = client_recv(CRef).
+ ?packet{msg = CEA}}
+ = client_recv(CRef),
+
+ [diameter_base_CEA | _] = Dict0:'#get-'(CEA),
+ [2001] = Dict0:'#get-'(['Result-Code'], CEA).
client_reject(Config) ->
- connect(Config, acct, [{capabilities_cb, fun client_capx/2}]).
+ connect(Config, acct, [{capabilities_cb, fun client_capx/2},
+ {applications, apps(Config)}]).
%% ===========================================================================
@@ -327,13 +349,21 @@ server_reject(Config, F, RC) ->
client_closed(Config, Host, F, RC) ->
true = diameter:subscribe(?CLIENT),
- OH = ?HOST(Host),
+ OH = host(Config, Host),
{CRef, _} = F(Config),
{'CEA', RC, ?caps{origin_host = {_, OH}}, ?packet{}}
= client_recv(CRef).
+srv(Config, Host) ->
+ "rfc" ++ N = atom_to_list(proplists:get_value(rfc, Config)),
+ [H, "srv" = S] = string:tokens(Host, "-"),
+ H ++ N ++ "-" ++ S.
+
+host(Config, Name) ->
+ ?HOST(srv(Config, Name)).
+
%% client_recv/1
client_recv(CRef) ->
@@ -364,6 +394,18 @@ client_capx(_, ?caps{origin_host = {[_,$_|"client_reject." ++ _], _}}) ->
%% ===========================================================================
+dict0(Config) ->
+ case proplists:get_value(rfc, Config) of
+ rfc3588 -> diameter_gen_base_rfc3588;
+ rfc6733 -> diameter_gen_base_rfc6733
+ end.
+
+apps(Config) ->
+ case proplists:get_value(rfc, Config) of
+ rfc3588 -> [base3588, acct3588];
+ rfc6733 -> [base6733, acct6733]
+ end.
+
host(Config) ->
{_, H} = lists:keyfind(host, 1, Config),
?HOST(H).
@@ -394,26 +436,32 @@ opts(PortNr, Opts) ->
{port, 0}]}
| Opts].
+lref(rfc3588, [LRef, _]) ->
+ LRef;
+lref(rfc6733, [_, LRef]) ->
+ LRef;
+
lref(Config, T) ->
- case ?util:read_priv(Config, ?MODULE) of
- {LRef, _} when T == base ->
- LRef;
- {_, LRef} when T == acct ->
- LRef
- end.
+ lref(proplists:get_value(rfc, Config),
+ case ?util:read_priv(Config, ?MODULE) of
+ {R, _} when T == base ->
+ R;
+ {_, R} when T == acct ->
+ R
+ end).
%% ===========================================================================
%% diameter callbacks
peer_up(?SERVER,
- {_, ?caps{origin_host = {"acct-srv." ++ _,
+ {_, ?caps{origin_host = {"acct" ++ _,
[_,$_|"client_reject." ++ _]}}},
State,
_) ->
State.
peer_down(?SERVER,
- {_, ?caps{origin_host = {"acct-srv." ++ _,
+ {_, ?caps{origin_host = {"acct" ++ _,
[_,$_|"client_reject." ++ _]}}},
State,
_) ->
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index fbd38067a8..18bd2cc190 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -27,7 +27,8 @@
-include("diameter.hrl").
--define(BASE, diameter_gen_base_rfc3588).
+-define(RFC3588, diameter_gen_base_rfc3588).
+-define(RFC6733, diameter_gen_base_rfc6733).
-define(BOOL, [true, false]).
-define(A, list_to_atom).
@@ -158,7 +159,8 @@ gen(M, messages, {Name, Code, Flags, _, _}) ->
Name = case M:msg_name(Code, lists:member('REQ', Flags)) of
N when Name /= 'answer-message' ->
N;
- '' when Name == 'answer-message', M == ?BASE ->
+ '' when Name == 'answer-message', (M == ?RFC3588
+ orelse M == ?RFC6733) ->
Name
end,
[] = arity(M, Name, Rname);
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
index 7c1c76f22a..18bdcb1f54 100644
--- a/lib/diameter/test/diameter_event_SUITE.erl
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -121,7 +121,6 @@ down(Config) ->
{applications, [?DICT_ACCT]},
{reconnect_timer, 5000}]),
start = event(Svc),
- {watchdog, Ref, _, {initial, down}, _} = event(Svc),
{closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{}}, _}
= event(Svc),
{reconnect, Ref, _} = event(Svc).
@@ -132,7 +131,6 @@ cea_timeout(Config) ->
{Svc, Ref} = connect(Config, [{capx_timeout, ?SERVER_CAPX_TMO div 2},
{reconnect_timer, 2*?SERVER_CAPX_TMO}]),
start = event(Svc),
- {watchdog, Ref, _, {initial, down}, _} = event(Svc),
{closed, Ref, {'CEA', timeout}, _} = event(Svc).
stop(_Config) ->
diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
index 2fde7b9fdb..51ccb1e6ec 100644
--- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl
+++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -355,7 +355,7 @@ open(Opts) ->
gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary,
{recbuf, 1 bsl 16}, {sndbuf, 1 bsl 16}
| Opts]).
-
+
%% assoc/1
assoc(Sock) ->
diff --git a/lib/diameter/test/diameter_length_SUITE.erl b/lib/diameter/test/diameter_length_SUITE.erl
new file mode 100644
index 0000000000..4e413e6a42
--- /dev/null
+++ b/lib/diameter/test/diameter_length_SUITE.erl
@@ -0,0 +1,288 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Tests of transport_opt() length_errors.
+%%
+
+-module(diameter_length_SUITE).
+
+-export([suite/0,
+ all/0,
+ groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+%% testcases
+-export([start/1,
+ send/1,
+ stop/1]).
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/6,
+ prepare_request/5,
+ handle_answer/6,
+ handle_error/6,
+ handle_request/3]).
+
+-include("diameter.hrl").
+-include("diameter_gen_base_rfc3588.hrl").
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+
+-define(CLIENT, "CLIENT").
+-define(SERVER, "SERVER").
+-define(REALM, "erlang.org").
+-define(HOST(Host, Realm), Host ++ [$.|Realm]).
+-define(DICT, diameter_gen_base_rfc3588).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Name),
+ [{'Origin-Host', Name ++ "." ++ ?REALM},
+ {'Origin-Realm', ?REALM},
+ {'Host-IP-Address', [{127,0,0,1}]},
+ {'Vendor-Id', 12345},
+ {'Product-Name', "OTP/diameter"},
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {application, [{dictionary, ?DICT},
+ {module, ?MODULE},
+ {answer_errors, callback}]}]).
+
+-define(SUCCESS,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
+-define(MISSING_AVP,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_MISSING_AVP').
+-define(INVALID_MESSAGE_LENGTH,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_MESSAGE_LENGTH').
+
+-define(LOGOUT,
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+
+-define(GROUPS, [exit, handle, discard]).
+
+-define(L, atom_to_list).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [{group, G} || G <- ?GROUPS].
+
+groups() ->
+ [{G, [], [start, send, stop]} || G <- ?GROUPS].
+
+init_per_suite(Config) ->
+ ok = diameter:start(),
+ Config.
+
+end_per_suite(_Config) ->
+ ok = diameter:stop().
+
+init_per_group(Group, Config) ->
+ [{group, Group} | Config].
+
+end_per_group(_, _) ->
+ ok.
+
+init_per_testcase(_Name, Config) ->
+ Config.
+
+end_per_testcase(_, _) ->
+ ok.
+
+origin(exit) -> 0;
+origin(handle) -> 1;
+origin(discard) -> 2;
+
+origin(0) -> exit;
+origin(1) -> handle;
+origin(2) -> discard.
+
+%% ===========================================================================
+
+%% start/1
+
+start(Config) ->
+ Group = proplists:get_value(group, Config),
+ ok = diameter:start_service(?SERVER, ?SERVICE(?L(Group))),
+ ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)),
+ LRef = ?util:listen(?SERVER,
+ tcp,
+ [{length_errors, Group}]),
+ ?util:connect(?CLIENT,
+ tcp,
+ LRef,
+ [{capabilities, [{'Origin-State-Id', origin(Group)}]}]).
+
+%% stop/1
+
+stop(_Config) ->
+ ok = diameter:remove_transport(?CLIENT, true),
+ ok = diameter:remove_transport(?SERVER, true),
+ ok = diameter:stop_service(?SERVER),
+ ok = diameter:stop_service(?CLIENT).
+
+%% send/1
+
+%% Server transport exits on messages of insuffient length.
+send(exit) ->
+ %% Transport exit is followed by failover but there's only one
+ %% transport to choose from.
+ {error, failover} = call(4);
+
+%% Server transport receives messages of insufficient length.
+send(handle) ->
+ %% Message Length too large: diameter_tcp flushes the request
+ %% when no additional bytes arrive.
+ #diameter_base_STA{'Result-Code' = ?INVALID_MESSAGE_LENGTH}
+ = call(4),
+ %% Another request answered as it should.
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(0),
+ %% Message Length conveniently small: the trailing optional
+ %% Origin-State-Id isn't included in the received request.
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(-12),
+ %% Server receives Origin-State-Id AVP as the first 12 bytes of
+ %% the next request: AVP <<Code:32, Flags:8, Len:24, Data:32>> is
+ %% interpreted as header <<Version:8, Len:24, Flags:8, Code:24,
+ %% ApplId: 32>>. In particular, the AVP Length 12 = 00001100 is
+ %% interpreted as Command Flags, so R=0 and the request is
+ %% interpreted as an unsolicited answer. Increase Message Length
+ %% to have the server receive all bytes sent thusfar.
+ {error, timeout}
+ = call(12),
+ %% Another request answered as it should.
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(0),
+ %% Shorten Message Length so much that that the server doesn't
+ %% receive the required Termination-Cause AVP.
+ #diameter_base_STA{'Result-Code' = ?MISSING_AVP}
+ = call(-24);
+
+%% Server transport discards message of insufficient length.
+send(discard) ->
+ %% First request times out when the server discards it but a
+ %% second succeeds since the transport remains up.
+ {error, timeout}
+ = call(4),
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(0);
+
+send(Config) ->
+ Group = proplists:get_value(group, Config),
+ put({?MODULE, group}, Group),
+ send(Group).
+
+%% ===========================================================================
+
+call(Delta) ->
+ Group = get({?MODULE, group}),
+ diameter:call(?CLIENT,
+ ?DICT,
+ #diameter_base_STR
+ {'Termination-Cause' = ?LOGOUT,
+ 'Auth-Application-Id' = ?DIAMETER_APP_ID_COMMON,
+ 'Origin-State-Id' = [7]},
+ [{extra, [Group, Delta]}]).
+
+%% ===========================================================================
+%% diameter callbacks
+
+%% peer_up/3
+
+peer_up(_SvcName, _Peer, State) ->
+ State.
+
+%% peer_down/3
+
+peer_down(_SvcName, _Peer, State) ->
+ State.
+
+%% pick_peer/6
+
+pick_peer([Peer], _, ?CLIENT, _State, _Group, _Delta) ->
+ {ok, Peer}.
+
+%% prepare_request/5
+
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, _Group, Delta) ->
+ {send, resize(Delta, prepare(Pkt, Caps))}.
+
+prepare(#diameter_packet{msg = Req0} = Pkt, Caps) ->
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, DR}}
+ = Caps,
+ Req = Req0#diameter_base_STR{'Session-Id' = diameter:session_id(OH),
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Destination-Realm' = DR},
+ diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}).
+
+resize(0, Pkt) ->
+ Pkt;
+resize(Delta, #diameter_packet{bin = Bin} = Pkt) ->
+ Pkt#diameter_packet{bin = resize(Delta, Bin)};
+
+resize(Delta, <<V, Len:24, T/binary>>) ->
+ <<V, (Len + Delta):24, T/binary>>.
+
+%% handle_answer/6
+
+handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Group, _Delta) ->
+ Pkt#diameter_packet.msg.
+
+%% handle_error/6
+
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Group, _Delta) ->
+ {error, Reason}.
+
+%% handle_request/3
+
+handle_request(Pkt, ?SERVER, {_Ref, Caps}) ->
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _},
+ origin_state_id = {_,[Id]}}
+ = Caps,
+ answer(origin(Id),
+ Pkt,
+ #diameter_base_STA{'Result-Code' = ?SUCCESS,
+ 'Session-Id' = diameter:session_id(OH),
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR}).
+
+answer(Group, #diameter_packet{errors = Es}, Ans) ->
+ answer(Group, Es, Ans);
+
+answer(_, [], Ans) ->
+ {reply, Ans};
+answer(Group, [RC|_], Ans)
+ when RC == ?INVALID_MESSAGE_LENGTH, Group == handle;
+ RC /= ?INVALID_MESSAGE_LENGTH ->
+ {reply, Ans}.
diff --git a/lib/diameter/test/diameter_stats_SUITE.erl b/lib/diameter/test/diameter_stats_SUITE.erl
index 8b7d8cb1b6..af52afb59c 100644
--- a/lib/diameter/test/diameter_stats_SUITE.erl
+++ b/lib/diameter/test/diameter_stats_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -69,7 +69,7 @@ reg(_) ->
true = ?stat:reg(Ref),
false = ?stat:reg(Ref). %% duplicate
-incr(_) ->
+incr(_) ->
Ref = '_',
Ctr = x,
false = ?stat:incr(Ctr), %% not registered,
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index b03a9ce4d1..6727e88b66 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -80,7 +80,9 @@
send_multiple_filters_2/1,
send_multiple_filters_3/1,
send_anything/1,
+ outstanding/1,
remove_transports/1,
+ empty/1,
stop_services/1,
stop/1]).
@@ -97,11 +99,21 @@
-include("diameter.hrl").
-include("diameter_gen_base_rfc3588.hrl").
-include("diameter_gen_base_accounting.hrl").
+%% The listening transports use RFC 3588 dictionaries, the client
+%% transports use either 3588 or 6733. (So can't use the record
+%% definitions in the latter case.)
%% ===========================================================================
-define(util, diameter_util).
+-define(A, list_to_atom).
+-define(L, atom_to_list).
+
+%% Don't use is_record/2 since dictionary hrl's aren't included.
+%% (Since they define conflicting reqcords with the same names.)
+-define(is_record(Rec, Name), (Name == element(1, Rec))).
+
-define(ADDR, {127,0,0,1}).
-define(CLIENT, "CLIENT").
@@ -111,9 +123,6 @@
-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
@@ -123,9 +132,14 @@
%% 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]).
+%% Which common dictionary to use in the clients.
+-define(RFCS, [rfc3588, rfc6733]).
+
+-record(group,
+ {client_encoding,
+ client_dict0,
+ server_encoding,
+ server_container}).
%% Not really what we should be setting unless the message is sent in
%% the common application but diameter doesn't care.
@@ -134,6 +148,17 @@
%% An Application-ID the server doesn't support.
-define(BAD_APP, 42).
+%% A common match when receiving answers in a client.
+-define(answer_message(SessionId, ResultCode),
+ ['answer-message',
+ {'Session-Id', SessionId},
+ {'Origin-Host', _},
+ {'Origin-Realm', _},
+ {'Result-Code', ResultCode}
+ | _]).
+-define(answer_message(ResultCode),
+ ?answer_message(_, ResultCode)).
+
%% Config for diameter:start_service/2.
-define(SERVICE(Name),
[{'Origin-Host', Name ++ "." ++ ?REALM},
@@ -147,7 +172,10 @@
| [{application, [{dictionary, D},
{module, ?MODULE},
{answer_errors, callback}]}
- || D <- [?BASE, ?ACCT]]]).
+ || D <- [diameter_gen_base_rfc3588,
+ diameter_gen_base_accounting,
+ diameter_gen_base_rfc6733,
+ diameter_gen_acct_rfc6733]]]).
-define(SUCCESS,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
@@ -186,11 +214,6 @@
-define(USER_MOVED,
?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED').
--define(A, list_to_atom).
--define(L, atom_to_list).
-
--define(NAME(A,B), ?A(?L(A) ++ "," ++ ?L(B))).
-
%% ===========================================================================
suite() ->
@@ -198,20 +221,27 @@ suite() ->
all() ->
[start, start_services, add_transports, result_codes]
- ++ [{group, ?util:name([R,C,A]), P} || R <- ?ENCODINGS,
- C <- ?CONTAINERS,
- A <- ?ENCODINGS,
- P <- [[], [parallel]]]
- ++ [remove_transports, stop_services, stop].
+ ++ [{group, ?util:name([R,D,A,C]), P} || R <- ?ENCODINGS,
+ D <- ?RFCS,
+ A <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ P <- [[], [parallel]]]
+ ++ [outstanding, remove_transports, empty, stop_services, stop].
groups() ->
Ts = tc(),
- [{?util:name([R,C,A]), [], Ts} || R <- ?ENCODINGS,
- C <- ?CONTAINERS,
- A <- ?ENCODINGS].
+ [{?util:name([R,D,A,C]), [], Ts} || R <- ?ENCODINGS,
+ D <- ?RFCS,
+ A <- ?ENCODINGS,
+ C <- ?CONTAINERS].
init_per_group(Name, Config) ->
- [{group, Name} | Config].
+ [R,D,A,C] = ?util:name(Name),
+ G = #group{client_encoding = R,
+ client_dict0 = dict0(D),
+ server_encoding = A,
+ server_container = C},
+ [{group, G} | Config].
end_per_group(_, _) ->
ok.
@@ -282,12 +312,33 @@ start_services(_Config) ->
| ?SERVICE(?CLIENT)]).
add_transports(Config) ->
- LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx/2}]),
- Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C},
- {capabilities, [osi(C)]}])
- || C <- ?CONNECTIONS],
+ LRef = ?util:listen(?SERVER,
+ tcp,
+ [{capabilities_cb, fun capx/2},
+ {applications, apps(rfc3588)}]),
+ Cs = [?util:connect(?CLIENT,
+ tcp,
+ LRef,
+ [{id, Id},
+ {capabilities, [{'Origin-State-Id', origin(Id)}]},
+ {applications, apps(D)}])
+ || A <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ D <- ?RFCS,
+ Id <- [{A,C}]],
+ %% The server uses the client's Origin-State-Id to decide how to
+ %% answer.
?util:write_priv(Config, "transport", [LRef | Cs]).
+apps(D0) ->
+ D = dict0(D0),
+ [acct(D), D].
+
+%% Ensure there are no outstanding requests in request table.
+outstanding(_Config) ->
+ [] = [T || T <- ets:tab2list(diameter_request),
+ is_atom(element(1,T))].
+
remove_transports(Config) ->
[LRef | Cs] = ?util:read_priv(Config, "transport"),
[?util:disconnect(?CLIENT, C, ?SERVER, LRef) || C <- Cs].
@@ -296,6 +347,10 @@ stop_services(_Config) ->
ok = diameter:stop_service(?CLIENT),
ok = diameter:stop_service(?SERVER).
+%% Ensure even transports have been removed from request table.
+empty(_Config) ->
+ [] = ets:tab2list(diameter_request).
+
stop(_Config) ->
ok = diameter:stop().
@@ -303,10 +358,6 @@ 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.
@@ -329,7 +380,7 @@ send_ok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 1}],
- #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS}
+ ['ACA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req).
%% Send an accounting ACR that the server answers badly to.
@@ -337,7 +388,7 @@ send_nok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 0}],
- #'diameter_base_answer-message'{'Result-Code' = ?INVALID_AVP_BITS}
+ ?answer_message(?INVALID_AVP_BITS)
= call(Config, Req).
%% Send an ACR and expect success.
@@ -345,7 +396,7 @@ send_eval(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 3}],
- #diameter_base_accounting_ACA{'Result-Code' = ?SUCCESS}
+ ['ACA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req).
%% Send an accounting ACR that the server tries to answer with an
@@ -362,46 +413,44 @@ send_protocol_error(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 4}],
- #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY}
+ ?answer_message(?TOO_BUSY)
= call(Config, Req).
%% Send an ASR with an arbitrary AVP and expect success and the same
%% AVP in the reply.
send_arbitrary(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{name = 'Class', value = "XXX"}]}],
- #diameter_base_ASA{'Result-Code' = ?SUCCESS,
- 'AVP' = Avps}
+ ['ASA', _SessionId, {'Result-Code', ?SUCCESS} | Avps]
= call(Config, Req),
- [#diameter_avp{name = 'Class',
- value = "XXX"}]
- = Avps.
+ {'AVP', [#diameter_avp{name = 'Class',
+ value = "XXX"}]}
+ = lists:last(Avps).
%% Send an unknown AVP (to some client) and check that it comes back.
send_unknown(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
is_mandatory = false,
data = <<17>>}]}],
- #diameter_base_ASA{'Result-Code' = ?SUCCESS,
- 'AVP' = Avps}
+ ['ASA', _SessionId, {'Result-Code', ?SUCCESS} | Avps]
= call(Config, Req),
- [#diameter_avp{code = 999,
- is_mandatory = false,
- data = <<17>>}]
- = Avps.
+ {'AVP', [#diameter_avp{code = 999,
+ is_mandatory = false,
+ data = <<17>>}]}
+ = lists:last(Avps).
%% Ditto but set the M flag.
send_unknown_mandatory(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
is_mandatory = true,
data = <<17>>}]}],
- #diameter_base_ASA{'Result-Code' = ?AVP_UNSUPPORTED,
- 'Failed-AVP' = Failed}
+ ['ASA', _SessionId, {'Result-Code', ?AVP_UNSUPPORTED} | Avps]
= call(Config, Req),
- [#'diameter_base_Failed-AVP'{'AVP' = Avps}] = Failed,
+ [#'diameter_base_Failed-AVP'{'AVP' = As}]
+ = proplists:get_value('Failed-AVP', Avps),
[#diameter_avp{code = 999,
is_mandatory = true,
data = <<17>>}]
- = Avps.
+ = As.
%% Send an STR that the server ignores.
send_noreply(Config) ->
@@ -411,32 +460,32 @@ send_noreply(Config) ->
%% Send an unsupported command and expect 3001.
send_unsupported(Config) ->
Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}],
- #'diameter_base_answer-message'{'Result-Code' = ?COMMAND_UNSUPPORTED}
+ ?answer_message(?COMMAND_UNSUPPORTED)
= call(Config, Req).
%% 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}
+ ?answer_message(?APPLICATION_UNSUPPORTED)
= call(Config, Req).
%% Send a request with the E bit set and expect 3008.
send_error_bit(Config) ->
Req = ['STR', {'Termination-Cause', ?BAD_ANSWER}],
- #'diameter_base_answer-message'{'Result-Code' = ?INVALID_HDR_BITS}
+ ?answer_message(?INVALID_HDR_BITS)
= call(Config, Req).
%% Send a bad version and check that we get 5011.
send_unsupported_version(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- #diameter_base_STA{'Result-Code' = ?UNSUPPORTED_VERSION}
+ ['STA', _SessionId, {'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}
+ ?answer_message(?INVALID_AVP_BITS)
= call(Config, Req).
%% Send a request containing an AVP length that doesn't match the
@@ -444,7 +493,7 @@ send_invalid_avp_bits(Config) ->
send_invalid_avp_length(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- #'diameter_base_STA'{'Result-Code' = ?INVALID_AVP_LENGTH}
+ ['STA', _SessionId, {'Result-Code', ?INVALID_AVP_LENGTH} | _]
= call(Config, Req).
%% Send a request containing 5xxx errors that the server rejects with
@@ -452,14 +501,14 @@ send_invalid_avp_length(Config) ->
send_invalid_reject(Config) ->
Req = ['STR', {'Termination-Cause', ?USER_MOVED}],
- #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY}
+ ?answer_message(?TOO_BUSY)
= call(Config, Req).
%% Send something long that will be fragmented by TCP.
send_long(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'User-Name', [lists:duplicate(1 bsl 20, $X)]}],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req).
%% Send something for which pick_peer finds no suitable peer.
@@ -484,14 +533,14 @@ send_any_1(Config) ->
send_any_2(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
- #'diameter_base_answer-message'{'Result-Code' = ?UNABLE_TO_DELIVER}
+ ?answer_message(?UNABLE_TO_DELIVER)
= call(Config, Req, [{filter, {any, [host, realm]}}]).
%% Send with a conjunctive filter.
send_all_1(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
Realm = lists:foldr(fun(C,A) -> [C,A] end, [], ?REALM),
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req, [{filter, {all, [{host, any},
{realm, Realm}]}}]).
send_all_2(Config) ->
@@ -509,8 +558,7 @@ send_timeout(Config) ->
%% received the Session-Id.
send_error(Config) ->
Req = ['RAR', {'Re-Auth-Request-Type', ?AUTHORIZE_AUTHENTICATE}],
- #'diameter_base_answer-message'{'Result-Code' = ?TOO_BUSY,
- 'Session-Id' = SId}
+ ?answer_message(SId, ?TOO_BUSY)
= call(Config, Req),
undefined /= SId.
@@ -520,10 +568,9 @@ send_detach(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
Ref = make_ref(),
ok = call(Config, Req, [{extra, [{self(), Ref}]}, detach]),
- #diameter_packet{msg = Rec, errors = []}
- = receive {Ref, T} -> T after 2000 -> false end,
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
- = Rec.
+ Ans = receive {Ref, T} -> T after 2000 -> false end,
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
+ = Ans.
%% Send a request which can't be encoded and expect {error, encode}.
send_encode_error(Config) ->
@@ -533,11 +580,11 @@ send_encode_error(Config) ->
send_destination_1(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Host', [?HOST(?SERVER, ?REALM)]}],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req, [{filter, {all, [host, realm]}}]).
send_destination_2(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, Req, [{filter, {all, [host, realm]}}]).
%% Send with filtering on and expect failure when specifying an
@@ -558,12 +605,12 @@ send_destination_4(Config) ->
send_destination_5(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Realm', "unknown.org"}],
- #'diameter_base_answer-message'{'Result-Code' = ?REALM_NOT_SERVED}
+ ?answer_message(?REALM_NOT_SERVED)
= call(Config, Req).
send_destination_6(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
- #'diameter_base_answer-message'{'Result-Code' = ?UNABLE_TO_DELIVER}
+ ?answer_message(?UNABLE_TO_DELIVER)
= call(Config, Req).
%% Specify an invalid option and expect failure.
@@ -597,7 +644,7 @@ send_bad_filter(Config, F) ->
%% Specify multiple filter options and expect them be conjunctive.
send_multiple_filters_1(Config) ->
Fun = fun(#diameter_caps{}) -> true end,
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= send_multiple_filters(Config, [host, {eval, Fun}]).
send_multiple_filters_2(Config) ->
E = {erlang, is_tuple, []},
@@ -608,7 +655,7 @@ send_multiple_filters_3(Config) ->
E2 = {erlang, is_tuple, []},
E3 = {erlang, is_record, [diameter_caps]},
E4 = [{erlang, is_record, []}, diameter_caps],
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= send_multiple_filters(Config, [{eval, E} || E <- [E1,E2,E3,E4]]).
send_multiple_filters(Config, Fs) ->
@@ -619,7 +666,7 @@ send_multiple_filters(Config, Fs) ->
%% only the return value from the prepare_request callback being
%% significant.
send_anything(Config) ->
- #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ ['STA', _SessionId, {'Result-Code', ?SUCCESS} | _]
= call(Config, anything).
%% ===========================================================================
@@ -629,56 +676,69 @@ call(Config, Req) ->
call(Config, Req, Opts) ->
Name = proplists:get_value(testcase, Config),
- [Encoding, C, E] = ?util:name(proplists:get_value(group, Config)),
+ #group{client_encoding = ReqEncoding,
+ client_dict0 = Dict0}
+ = Group
+ = proplists:get_value(group, Config),
diameter:call(?CLIENT,
- dict(Req),
- msg(Req, Encoding),
- [{extra, [Name, client(E,C)]} | Opts]).
+ dict(Req, Dict0),
+ msg(Req, ReqEncoding, Dict0),
+ [{extra, [Name, Group]} | Opts]).
-client(E, C) ->
- list_to_atom([$c, $0 + 2*codec(E) + container(C)]).
+origin({A,C}) ->
+ 2*codec(A) + container(C);
-client(N) ->
- {codec(N bsr 1), container(N rem 2)}.
+origin(N) ->
+ {codec(N band 2), container(N rem 2)}.
+
+%% Map booleans, but the readable atoms are part of (constructed)
+%% group names, so it's good that they're readable.
codec(record) -> 0;
-codec(list) -> 1;
+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.
+codec(_) -> list.
container(pkt) -> 0;
container(msg) -> 1;
container(0) -> pkt;
-container(1) -> msg.
+container(_) -> msg.
-msg([H|T], record)
+msg([H|_] = Msg, record = E, diameter_gen_base_rfc3588)
when H == 'ACR';
H == 'ACA' ->
- ?ACCT:'#new-'(?ACCT:msg2rec(H), T);
-msg([H|T], record) ->
- ?BASE:'#new-'(?BASE:msg2rec(H), T);
-msg(T, _) ->
- T.
-
-dict(['ACR' | _]) ->
- ?ACCT;
-dict(#diameter_base_accounting_ACR{}) ->
- ?ACCT;
-dict(_) ->
- ?BASE.
+ msg(Msg, E, diameter_gen_base_accounting);
+msg([H|_] = Msg, record = E, diameter_gen_base_rfc6733)
+ when H == 'ACR';
+ H == 'ACA' ->
+ msg(Msg, E, diameter_gen_acct_rfc6733);
+msg([H|T], record, Dict) ->
+ Dict:'#new-'(Dict:msg2rec(H), T);
+msg(Msg, _, _) ->
+ Msg.
+
+dict0(D) ->
+ ?A("diameter_gen_base_" ++ ?L(D)).
+
+dict(Msg, Dict0)
+ when 'ACR' == hd(Msg);
+ 'ACA' == hd(Msg);
+ ?is_record(Msg, diameter_base_accounting_ACR);
+ ?is_record(Msg, diameter_base_accounting_ACA) ->
+ acct(Dict0);
+dict(_, Dict0) ->
+ Dict0.
+
+acct(diameter_gen_base_rfc3588) ->
+ diameter_gen_base_accounting;
+acct(diameter_gen_base_rfc6733) ->
+ diameter_gen_acct_rfc6733.
%% Set only values that aren't already.
-set([H|T], Vs) ->
+set(_, [H|T], Vs) ->
[H | Vs ++ T];
-set(#diameter_base_accounting_ACR{} = Rec, Vs) ->
- set(Rec, Vs, ?ACCT);
-set(Rec, Vs) ->
- set(Rec, Vs, ?BASE).
-
-set(Rec, Vs, Dict) ->
+set(#group{client_dict0 = Dict0} = _Group, Rec, Vs) ->
+ Dict = dict(Rec, Dict0),
lists:foldl(fun({F,_} = FV, A) ->
set(Dict, Dict:'#get-'(F, A), FV, A)
end,
@@ -707,17 +767,18 @@ peer_down(_SvcName, _Peer, State) ->
%% pick_peer/6-7
-pick_peer(Peers, _, ?CLIENT, _State, Name, Id)
+pick_peer(Peers, _, ?CLIENT, _State, Name, Group)
when Name /= send_detach ->
- find(Id, Peers).
+ find(Group, Peers).
pick_peer(_Peers, _, ?CLIENT, _State, send_nopeer, _, ?EXTRA) ->
false;
-pick_peer(Peers, _, ?CLIENT, _State, send_detach, Id, {_,_}) ->
- find(Id, Peers).
+pick_peer(Peers, _, ?CLIENT, _State, send_detach, Group, {_,_}) ->
+ find(Group, Peers).
-find(Id, Peers) ->
+find(#group{server_encoding = A, server_container = C}, Peers) ->
+ Id = {A,C},
[P] = [P || P <- Peers, id(Id, P)],
{ok, P}.
@@ -731,39 +792,40 @@ id(Id, {Pid, _Caps}) ->
prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard, _) ->
{discard, unprepared};
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name, _) ->
- {send, prepare(Pkt, Caps, Name)}.
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name, Group) ->
+ {send, prepare(Pkt, Caps, Name, Group)}.
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _, _) ->
- {eval_packet, {send, prepare(Pkt, Caps)}, [fun log/2, detach]}.
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, Group, _) ->
+ {eval_packet, {send, prepare(Pkt, Caps, Group)}, [fun log/2, detach]}.
log(#diameter_packet{} = P, T) ->
io:format("~p: ~p~n", [T,P]).
-%% prepare/3
+%% prepare/4
-prepare(Pkt, Caps, send_invalid_avp_bits) ->
- Req = prepare(Pkt, Caps),
+prepare(Pkt, Caps, send_invalid_avp_bits, #group{client_dict0 = Dict0}
+ = Group) ->
+ Req = prepare(Pkt, Caps, Group),
%% 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}),
+ = diameter_codec:encode(Dict0, 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)
+prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
when N == send_invalid_avp_length;
N == send_invalid_reject ->
- Req = prepare(Pkt, Caps),
+ Req = prepare(Pkt, Caps, Group),
%% 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}),
+ = diameter_codec:encode(Dict0, 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
@@ -774,107 +836,111 @@ prepare(Pkt, Caps, N)
0:32/integer,
T/binary>>};
-prepare(Pkt, Caps, send_unsupported) ->
- Req = prepare(Pkt, Caps),
+prepare(Pkt, Caps, send_unsupported, #group{client_dict0 = Dict0} = Group) ->
+ Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>}
= E
- = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ = diameter_codec:encode(Dict0, 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),
+prepare(Pkt, Caps, send_unsupported_app, #group{client_dict0 = Dict0}
+ = Group) ->
+ Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>}
= E
- = diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
+ = diameter_codec:encode(Dict0, Pkt#diameter_packet{msg = Req}),
E#diameter_packet{bin = <<H/binary, ?BAD_APP:32/integer, T/binary>>};
-prepare(Pkt, Caps, send_error_bit) ->
+prepare(Pkt, Caps, send_error_bit, Group) ->
#diameter_packet{header = Hdr} = Pkt,
Pkt#diameter_packet{header = Hdr#diameter_header{is_error = true},
- msg = prepare(Pkt, Caps)};
+ msg = prepare(Pkt, Caps, Group)};
-prepare(Pkt, Caps, send_unsupported_version) ->
+prepare(Pkt, Caps, send_unsupported_version, Group) ->
#diameter_packet{header = Hdr} = Pkt,
Pkt#diameter_packet{header = Hdr#diameter_header{version = 42},
- msg = prepare(Pkt, Caps)};
+ msg = prepare(Pkt, Caps, Group)};
-prepare(Pkt, Caps, send_anything) ->
+prepare(Pkt, Caps, send_anything, Group) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- prepare(Pkt#diameter_packet{msg = Req}, Caps);
+ prepare(Pkt#diameter_packet{msg = Req}, Caps, Group);
-prepare(Pkt, Caps, _Name) ->
- prepare(Pkt, Caps).
+prepare(Pkt, Caps, _Name, Group) ->
+ prepare(Pkt, Caps, Group).
-%% prepare/2
+%% prepare/3
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_accounting_ACR);
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_accounting_ACR);
'ACR' == hd(Req) ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Realm', DR}]);
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Realm', DR}]);
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_ASR);
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_ASR);
'ASR' == hd(Req) ->
#diameter_caps{origin_host = {OH, DH},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR},
- {'Auth-Application-Id', ?APP_ID}]);
-
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_STR);
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', DH},
+ {'Destination-Realm', DR},
+ {'Auth-Application-Id', ?APP_ID}]);
+
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_STR);
'STR' == hd(Req) ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Realm', DR},
- {'Auth-Application-Id', ?APP_ID}]);
-
-prepare(#diameter_packet{msg = Req}, Caps)
- when is_record(Req, diameter_base_RAR);
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Realm', DR},
+ {'Auth-Application-Id', ?APP_ID}]);
+
+prepare(#diameter_packet{msg = Req}, Caps, Group)
+ when ?is_record(Req, diameter_base_RAR);
'RAR' == hd(Req) ->
#diameter_caps{origin_host = {OH, DH},
origin_realm = {OR, DR}}
= Caps,
- set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR},
- {'Auth-Application-Id', ?APP_ID}]).
+ set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', DH},
+ {'Destination-Realm', DR},
+ {'Auth-Application-Id', ?APP_ID}]).
%% prepare_retransmit/5
-prepare_retransmit(_Pkt, false, _Peer, _Name, _Id) ->
+prepare_retransmit(_Pkt, false, _Peer, _Name, _Group) ->
discard.
%% handle_answer/6-7
-handle_answer(Pkt, Req, ?CLIENT, Peer, Name, _Id) ->
- answer(Pkt, Req, Peer, Name).
+handle_answer(Pkt, Req, ?CLIENT, Peer, Name, Group) ->
+ answer(Pkt, Req, Peer, Name, Group).
-handle_answer(Pkt, _Req, ?CLIENT, _Peer, send_detach, _Id, {Pid, Ref}) ->
- Pid ! {Ref, Pkt}.
+handle_answer(Pkt, Req, ?CLIENT, Peer, send_detach = Name, Group, X) ->
+ {Pid, Ref} = X,
+ Pid ! {Ref, answer(Pkt, Req, Peer, Name, Group)}.
-answer(Pkt, Req, _Peer, Name) ->
- #diameter_packet{header = H, msg = Rec, errors = Es} = Pkt,
- ApplId = app(Req, Name),
+answer(Pkt, Req, _Peer, Name, #group{client_dict0 = Dict0}) ->
+ #diameter_packet{header = H, msg = Ans, errors = Es} = Pkt,
+ ApplId = app(Req, Name, Dict0),
#diameter_header{application_id = ApplId} = H, %% assert
- answer(Rec, Es, Name).
+ Dict = dict(Ans, Dict0),
+ [R | Vs] = Dict:'#get-'(answer(Ans, Es, Name)),
+ [Dict:rec2msg(R) | Vs].
answer(Rec, [_|_], N)
when N == send_invalid_avp_bits;
@@ -884,15 +950,15 @@ answer(Rec, [_|_], N)
answer(Rec, [], _) ->
Rec.
-app(_, send_unsupported_app) ->
+app(_, send_unsupported_app, _) ->
?BAD_APP;
-app(Req, _) ->
- Dict = dict(Req),
+app(Req, _, Dict0) ->
+ Dict = dict(Req, Dict0),
Dict:id().
%% handle_error/6
-handle_error(Reason, _Req, ?CLIENT, _Peer, _Name, _Id) ->
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Name, _Group) ->
{error, Reason}.
%% handle_request/3
@@ -907,13 +973,13 @@ handle_request(#diameter_packet{header = H, msg = M}, ?SERVER, {_Ref, Caps}) ->
{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)).
+ #diameter_caps{origin_state_id = {_,[Id]}} = Caps,
+ answer(origin(Id), 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({A,C}, {reply, Ans}) ->
+ answer(C, {reply, msg(Ans, A, diameter_gen_base_rfc3588)});
answer(pkt, {reply, Ans}) ->
{reply, #diameter_packet{msg = Ans}};
answer(_, T) ->
@@ -987,7 +1053,8 @@ request(#diameter_base_ASR{'Session-Id' = SId,
{'AVP', Avps}]};
%% send_invalid_reject
-request(#diameter_base_STR{'Termination-Cause' = ?USER_MOVED}, _Caps) ->
+request(#diameter_base_STR{'Termination-Cause' = ?USER_MOVED},
+ _Caps) ->
{protocol_error, ?TOO_BUSY};
%% send_noreply
@@ -997,13 +1064,13 @@ request(#diameter_base_STR{'Termination-Cause' = T},
discard;
%% send_destination_5
-request(#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};
%% send_destination_6
-request(#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};
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index 7ce09e93ca..e1e166b834 100644
--- a/lib/diameter/test/diameter_watchdog_SUITE.erl
+++ b/lib/diameter/test/diameter_watchdog_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -127,13 +127,7 @@ end_per_suite(_Config) ->
%% ===========================================================================
%% Test the watchdog state machine for the required failover, failback
-%% and reopen behaviour. Do this by having the testcase replace
-%% diameter_service and start watchdogs, and having this module
-%% implement a transport process that plays the role of the peer
-%% Diameter node.
-
-%reopen(_) ->
-% reopen(connect, ?WD(10000), 1, 'DWR');
+%% and reopen behaviour by examining watchdog events.
reopen(_) ->
[] = run([[reopen, T, Wd, N, M]
@@ -349,7 +343,7 @@ recv_reopen(listen, Ref, _, _) ->
reg(Type, Ref, SvcName, T) ->
TPid = tpid(Type, Ref, diameter:service_info(SvcName, transport)),
true = diameter_reg:add_new({?MODULE, TPid, T}).
-
+
%% tpid/3
tpid(connect, Ref, [[{ref, Ref},
@@ -375,7 +369,7 @@ tpid(listen, Ref, [[{ref, Ref},
{port, [{owner, TPid} | _]}
| _]]
= lists:filter(fun([{watchdog, {_,_,S}} | _]) ->
- S == okay orelse S == reopen
+ S == okay orelse S == reopen
end,
As),
TPid.
@@ -432,7 +426,7 @@ send({SvcName, {_,_,_} = T}, Sock, Bin) ->
putr(origin, [OH, OR]),
putr(config, T),
send(Sock, Bin);
-
+
%% Discard DWA, failback after another timeout in the peer.
send({Wd, 0 = No, Msg}, Sock, Bin) ->
Origin = getr(origin),
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index 80b1769d04..f575085843 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2012. 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
@@ -41,7 +41,8 @@ MODULES = \
diameter_tls_SUITE \
diameter_failover_SUITE \
diameter_dpr_SUITE \
- diameter_event_SUITE
+ diameter_event_SUITE \
+ diameter_length_SUITE
HRL_FILES = \
diameter_ct.hrl