aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter')
-rw-r--r--lib/diameter/doc/src/Makefile4
-rw-r--r--lib/diameter/doc/src/notes.xml16
-rw-r--r--lib/diameter/src/Makefile10
-rw-r--r--lib/diameter/src/base/diameter.appup.src29
-rw-r--r--lib/diameter/src/base/diameter_codec.erl8
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl5
-rw-r--r--lib/diameter/src/base/diameter_service.erl31
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl182
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl258
-rw-r--r--lib/diameter/test/modules.mk3
-rw-r--r--lib/diameter/vsn.mk4
11 files changed, 468 insertions, 82 deletions
diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile
index 8ad38ba0d5..0cbe1f000f 100644
--- a/lib/diameter/doc/src/Makefile
+++ b/lib/diameter/doc/src/Makefile
@@ -173,8 +173,8 @@ release_spec:
depend.mk: depend.sed Makefile seealso.ent \
$(XML_REF_FILES) $(XML_CHAPTER_FILES)
- sed -f seehere.sed seealso.ent > seehere.ent
- (for f in $(XML_REF_FILES) $(XML_CHAPTER_FILES); do \
+ $(gen_verbose)sed -f seehere.sed seealso.ent > seehere.ent
+ $(V_at)(for f in $(XML_REF_FILES) $(XML_CHAPTER_FILES); do \
sed -f $< $$f | sed "s@%FILE%@`basename $$f .xml`@g"; \
done) \
> $@
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index d448b01eba..d241e2bd19 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -42,6 +42,22 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>Diameter 1.3.1</title>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Fix function clause resulting from use of an eval
+ callback.</p>
+ <p>
+ Own Id: OTP-10685</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Diameter 1.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 060659bce9..60bb7e6a10 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -119,7 +119,7 @@ ERL_COMPILE_FLAGS += \
# erl/hrl from dictionary file.
gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
- ../bin/diameterc -o gen -i $(EBIN) $<
+ $(dia_verbose)../bin/diameterc -o gen -i $(EBIN) $<
opt: $(TARGET_FILES)
@@ -128,17 +128,17 @@ debug:
# The dictionary parser.
gen/$(DICT_YRL).erl: compiler/$(DICT_YRL).yrl
- $(ERLC) -Werror -o $(@D) $<
+ $(yecc_verbose)$(ERLC) -Werror -o $(@D) $<
# Generate the app file.
$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
- M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
+ $(gen_verbose)M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
sed -e 's;%VSN%;$(VSN);' \
-e "s;%MODULES%;$$M;" \
$< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
- sed -e 's;%VSN%;$(VSN);' $< > $@
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
app: $(APP_TARGET) $(APPUP_TARGET)
dict: $(DICT_ERLS)
@@ -254,7 +254,7 @@ depend: depend.mk
# Generate dependencies makefile.
depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
- (for f in $(MODULES); do \
+ $(gen_verbose)(for f in $(MODULES); do \
(echo $$f; cat $$f.erl) | sed -f $<; \
done) \
> $@
diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src
index 5655f98c1b..a04a387918 100644
--- a/lib/diameter/src/base/diameter.appup.src
+++ b/lib/diameter/src/base/diameter.appup.src
@@ -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
@@ -24,27 +24,9 @@
{"0.10", [{restart_application, diameter}]},
{"1.0", [{restart_application, diameter}]},
{"1.1", [{restart_application, diameter}]},
- {"1.2", [{load, diameter},
- {load, diameter_capx},
- {load, diameter_codec},
- {load, diameter_peer},
- {load, diameter_reg},
- %% order significant from here
- {load, diameter_session},
- {load, diameter_peer_fsm},
- {load, diameter_service},
- {load, diameter_watchdog},
- {load, diameter_config}]},
- {"1.2.1", [{load, diameter},
- {load, diameter_capx},
- {load, diameter_peer},
- {load, diameter_reg},
- %% order significant from here
- {load, diameter_session},
- {load, diameter_peer_fsm},
- {load, diameter_service},
- {load, diameter_watchdog},
- {load, diameter_config}]}
+ {"1.2", [{restart_application, diameter}]},
+ {"1.2.1", [{restart_application, diameter}]},
+ {"1.3", [{load_module, diameter_service}]}
],
[
{"0.9", [{restart_application, diameter}]},
@@ -52,6 +34,7 @@
{"1.0", [{restart_application, diameter}]},
{"1.1", [{restart_application, diameter}]},
{"1.2", [{restart_application, diameter}]},
- {"1.2.1", [{restart_application, diameter}]}
+ {"1.2.1", [{restart_application, diameter}]},
+ {"1.3", [{load_module, diameter_service}]}
]
}.
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index a94d37f7a8..0b0bfe3f0a 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -193,9 +193,11 @@ encode_avps(Avps) ->
msg_header(Mod, 'answer-message' = MsgName, Header) ->
?BASE = Mod,
- #diameter_header{cmd_code = Code} = Header,
- {_, Flags, ApplId} = ?BASE:msg_header(MsgName),
- {Code, Flags, ApplId};
+ #diameter_header{application_id = Aid,
+ cmd_code = Code}
+ = Header,
+ {-1, Flags, ?DIAMETER_APP_ID_COMMON} = ?BASE:msg_header(MsgName),
+ {Code, Flags, Aid};
msg_header(Mod, MsgName, _) ->
Mod:msg_header(MsgName).
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index c4320fcb99..858870566f 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -388,8 +388,9 @@ transition({diameter, {recv, Pkt}}, S) ->
recv(Pkt, S);
%% Timeout when still in the same state ...
-transition({timeout, PS}, #state{state = PS}) ->
- {stop, {capx(PS), timeout}};
+transition({timeout = T, PS}, #state{state = PS} = S) ->
+ close({capx(PS), T}, S),
+ stop;
%% ... or not.
transition({timeout, _}, _) ->
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index 91384b8b91..b5584ca0d0 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.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
@@ -859,10 +859,10 @@ i(SvcName) ->
true = ets:insert_new(?STATE_TABLE, S),
%% Start fsms for each transport.
+ send_event(SvcName, start),
lists:foreach(fun(T) -> start_fsm(T,S) end, CL),
init_shared(S),
- send_event(SvcName, start),
S.
cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
@@ -1457,7 +1457,7 @@ make_prepare_packet(Mask, #diameter_packet{header = Hdr} = Pkt) ->
make_prepare_packet(Mask, Msg) ->
make_prepare_packet(Mask, #diameter_packet{msg = Msg}).
-%% make_prepare_header/1
+%% make_prepare_header/2
make_prepare_header(Mask, undefined) ->
Seq = diameter_session:sequence(Mask),
@@ -1465,10 +1465,11 @@ make_prepare_header(Mask, undefined) ->
hop_by_hop_id = Seq});
make_prepare_header(Mask, #diameter_header{end_to_end_id = undefined,
- hop_by_hop_id = undefined}) ->
+ hop_by_hop_id = undefined}
+ = H) ->
Seq = diameter_session:sequence(Mask),
- make_prepare_header(#diameter_header{end_to_end_id = Seq,
- hop_by_hop_id = Seq});
+ make_prepare_header(H#diameter_header{end_to_end_id = Seq,
+ hop_by_hop_id = Seq});
make_prepare_header(Mask, #diameter_header{end_to_end_id = undefined} = H) ->
Seq = diameter_session:sequence(Mask),
@@ -2053,15 +2054,21 @@ request_cb({eval_packet, RC, F}, App, Mask, T, TC, Fs, Pkt) ->
request_cb(RC, App, Mask, T, TC, [F|Fs], Pkt);
request_cb({eval, RC, F}, App, Mask, T, TC, Fs, Pkt) ->
- request_cb(RC, App, Mask, T, TC, Pkt, Fs),
+ request_cb(RC, App, Mask, T, TC, Fs, Pkt),
diameter_lib:eval(F).
%% protocol_error/5
protocol_error(RC, {_, OH, OR}, TPid, Fs, Pkt) ->
- #diameter_packet{avps = Avps} = Pkt,
+ #diameter_packet{avps = Avps, errors = Es} = Pkt,
?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Avps), ?BASE, TPid, Fs, Pkt).
+ reply(answer_message({OH, OR, RC}, Avps),
+ ?BASE,
+ TPid,
+ Fs,
+ Pkt#diameter_packet{errors = [RC | Es]}).
+%% Note that reply/5 may set the result code once more. It's set in
+%% answer_message/2 in case reply/5 doesn't.
%% protocol_error/4
@@ -2174,7 +2181,8 @@ is_loop(Code, Vid, OH, Avps) ->
%%
%% Send a locally originating reply.
-%% Skip the setting of Result-Code and Failed-AVP's below.
+%% Skip the setting of Result-Code and Failed-AVP's below. This is
+%% currently undocumented.
reply([Msg], Dict, TPid, Fs, Pkt)
when is_list(Msg);
is_tuple(Msg) ->
@@ -2242,6 +2250,9 @@ rc(RC) ->
%% rc/4
+rc(#diameter_packet{msg = Rec} = Pkt, RC, Failed, Dict) ->
+ Pkt#diameter_packet{msg = rc(Rec, RC, Failed, Dict)};
+
rc(Rec, RC, Failed, Dict)
when is_integer(RC) ->
set(Rec,
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
new file mode 100644
index 0000000000..7c1c76f22a
--- /dev/null
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -0,0 +1,182 @@
+%%
+%% %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 events sent as a consequence of diameter:subscribe/1.
+%% Watchdog events are dealt with more extensively in the watchdog
+%% suite.
+%%
+
+-module(diameter_event_SUITE).
+
+-export([suite/0,
+ all/0,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+%% testcases
+-export([start/1,
+ start_server/1,
+ up/1,
+ down/1,
+ cea_timeout/1,
+ stop/1]).
+
+-include("diameter.hrl").
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+
+-define(ADDR, {127,0,0,1}).
+-define(REALM, "REALM").
+
+-define(SERVER, "SERVER.SERVER-REALM").
+-define(CLIENT, "CLIENT.CLIENT-REALM").
+
+-define(DICT_COMMON, ?DIAMETER_DICT_COMMON).
+-define(DICT_ACCT, ?DIAMETER_DICT_ACCOUNTING).
+
+-define(SERVER_CAPX_TMO, 6000).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Host, Dicts),
+ [{'Origin-Host', Host},
+ {'Origin-Realm', realm(Host)},
+ {'Host-IP-Address', [?ADDR]},
+ {'Vendor-Id', 12345},
+ {'Product-Name', "OTP/diameter"},
+ {'Acct-Application-Id', [D:id() || D <- Dicts]}
+ | [{application, [{dictionary, D},
+ {module, #diameter_callback{}}]}
+ || D <- Dicts]]).
+
+%% Diameter Result-Code's:
+-define(NO_COMMON_APP, 5010).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [start,
+ start_server,
+ up,
+ down,
+ cea_timeout,
+ stop].
+
+init_per_testcase(Name, Config) ->
+ [{name, Name} | Config].
+
+end_per_testcase(_, _) ->
+ ok.
+
+%% ===========================================================================
+%% start/stop testcases
+
+start(_Config) ->
+ ok = diameter:start().
+
+start_server(Config) ->
+ diameter:subscribe(?SERVER),
+ ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER, [?DICT_COMMON])),
+ LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx_cb/2},
+ {capx_timeout, ?SERVER_CAPX_TMO}]),
+ [PortNr] = ?util:lport(tcp, LRef, 20),
+ ?util:write_priv(Config, portnr, PortNr),
+ start = event(?SERVER).
+
+%% Connect with matching capabilities and expect the connection to
+%% come up.
+up(Config) ->
+ {Svc, Ref} = connect(Config, []),
+ start = event(Svc),
+ {up, Ref, {_,_Caps}, _Config, #diameter_packet{}} = event(Svc),
+ {watchdog, Ref, _, {initial, okay}, _} = event(Svc).
+
+%% Connect with non-matching capabilities and expect CEA from the peer
+%% to indicate as much and then for the transport to be restarted
+%% (after reconnect_timer).
+down(Config) ->
+ {Svc, Ref} = connect(Config, [{capabilities, [{'Acct-Application-Id',
+ [?DICT_ACCT:id()]}]},
+ {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).
+
+%% Connect with matching capabilities but have the server delay its
+%% CEA and cause the client to timeout.
+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) ->
+ ok = diameter:stop().
+
+%% ----------------------------------------
+
+%% Keep the server from sending CEA until the client has timed out.
+capx_cb(_, #diameter_caps{origin_host = {_, "cea_timeout-" ++ _}}) ->
+ receive after ?SERVER_CAPX_TMO -> ok end;
+
+%% Or not.
+capx_cb(_, _Caps) ->
+ ok.
+
+%% ----------------------------------------
+
+%% Use the testcase name to construct Origin-Host of the client so
+%% that the server can match on it in capx_cb/2.
+connect(Config, Opts) ->
+ Pre = atom_to_list(proplists:get_value(name, Config)),
+ Name = Pre ++ uniq() ++ ?CLIENT,
+ diameter:subscribe(Name),
+ ok = start_service(Name, ?SERVICE(Name, [?DICT_COMMON, ?DICT_ACCT])),
+ {ok, Ref} = diameter:add_transport(Name, opts(Config, Opts)),
+ {Name, Ref}.
+
+uniq() ->
+ {MS,S,US} = now(),
+ lists:flatten(io_lib:format("-~p-~p-~p-", [MS,S,US])).
+
+event(Name) ->
+ receive #diameter_event{service = Name, info = T} -> T end.
+
+start_service(Name, Opts) ->
+ diameter:start_service(Name, [{monitor, self()} | Opts]).
+
+opts(Config, Opts) ->
+ PortNr = ?util:read_priv(Config, portnr),
+
+ {connect, [{transport_module, diameter_tcp},
+ {transport_config, [{ip, ?ADDR}, {port, 0},
+ {raddr, ?ADDR}, {rport, PortNr}]}
+ | Opts]}.
+
+realm(Host) ->
+ tl(lists:dropwhile(fun(C) -> C /= $. end, Host)).
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index c157b0e304..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-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
@@ -38,7 +38,9 @@
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,
@@ -47,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,
@@ -112,17 +117,23 @@
%% 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.
+%% How to construct messages, as record or list.
-define(ENCODINGS, [list, record]).
-%% Identifers for client connections.
--define(CONNECTIONS, [c1,c2,c3]).
+%% 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},
@@ -158,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').
@@ -170,6 +183,8 @@
?'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).
@@ -183,14 +198,17 @@ suite() ->
all() ->
[start, start_services, add_transports, result_codes]
- ++ [{group, ?util:name([E,C]), P} || E <- ?ENCODINGS,
- C <- ?CONNECTIONS,
- 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(),
- [{?util:name([E,C]), [], Ts} || E <- ?ENCODINGS, C <- ?CONNECTIONS].
+ [{?util:name([R,C,A]), [], Ts} || R <- ?ENCODINGS,
+ C <- ?CONTAINERS,
+ A <- ?ENCODINGS].
init_per_group(Name, Config) ->
[{group, Name} | Config].
@@ -209,7 +227,9 @@ end_per_testcase(_, _) ->
tc() ->
[send_ok,
send_nok,
+ send_eval,
send_bad_answer,
+ send_protocol_error,
send_arbitrary,
send_unknown,
send_unknown_mandatory,
@@ -218,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,
@@ -260,7 +283,9 @@ start_services(_Config) ->
add_transports(Config) ->
LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx/2}]),
- Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C}]) || C <- ?CONNECTIONS],
+ Cs = [?util:connect(?CLIENT, tcp, LRef, [{id, C},
+ {capabilities, [osi(C)]}])
+ || C <- ?CONNECTIONS],
?util:write_priv(Config, "transport", [LRef | Cs]).
remove_transports(Config) ->
@@ -278,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, 3009, 5001, 5011}
+ {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011, 5014}
= {?SUCCESS,
?COMMAND_UNSUPPORTED,
?UNABLE_TO_DELIVER,
@@ -292,13 +321,14 @@ result_codes(_Config) ->
?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_accounting_ACA{'Result-Code' = ?SUCCESS}
= call(Config, Req).
@@ -306,10 +336,18 @@ send_ok(Config) ->
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.
@@ -318,6 +356,15 @@ send_bad_answer(Config) ->
{'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
%% AVP in the reply.
send_arbitrary(Config) ->
@@ -385,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},
@@ -559,21 +629,44 @@ call(Config, Req) ->
call(Config, Req, Opts) ->
Name = proplists:get_value(testcase, Config),
- [Encoding, Client] = ?util:name(proplists:get_value(group, Config)),
+ [Encoding, C, E] = ?util:name(proplists:get_value(group, Config)),
diameter:call(?CLIENT,
dict(Req),
- req(Req, Encoding),
- [{extra, [Name, Client]} | Opts]).
+ 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)}.
-req(['ACR' = H | T], record) ->
+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.
+
+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);
-req([H|T], record) ->
+msg([H|T], record) ->
?BASE:'#new-'(?BASE:msg2rec(H), T);
-req(T, _) ->
+msg(T, _) ->
T.
dict(['ACR' | _]) ->
?ACCT;
+dict(#diameter_base_accounting_ACR{}) ->
+ ?ACCT;
dict(_) ->
?BASE.
@@ -647,6 +740,40 @@ prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_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>>}
@@ -659,7 +786,7 @@ prepare(Pkt, Caps, send_unsupported_app) ->
#diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>}
= E
= diameter_codec:encode(?BASE, Pkt#diameter_packet{msg = Req}),
- E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>};
+ E#diameter_packet{bin = <<H/binary, ?BAD_APP:32/integer, T/binary>>};
prepare(Pkt, Caps, send_error_bit) ->
#diameter_packet{header = Hdr} = Pkt,
@@ -678,6 +805,8 @@ 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_accounting_ACR);
'ACR' == hd(Req) ->
@@ -741,9 +870,26 @@ handle_answer(Pkt, Req, ?CLIENT, Peer, Name, _Id) ->
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.
+app(_, send_unsupported_app) ->
+ ?BAD_APP;
+app(Req, _) ->
+ Dict = dict(Req),
+ Dict:id().
+
%% handle_error/6
handle_error(Reason, _Req, ?CLIENT, _Peer, _Name, _Id) ->
@@ -761,12 +907,24 @@ handle_request(#diameter_packet{header = H, msg = M}, ?SERVER, {_Ref, Caps}) ->
{V,B} = ?CLIENT_MASK,
V = EI bsr B, %% assert
V = HI bsr B, %%
- request(M, Caps).
+ #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.
+%% 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},
@@ -779,12 +937,27 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}],
- {reply, #diameter_packet{header = #diameter_header{is_error = true},%% not
+ {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' = RN},
+ 'Accounting-Record-Number' = 1 = RN},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, ['ACA', {'Result-Code', ?SUCCESS},
@@ -794,26 +967,42 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{'Accounting-Record-Type', RT},
{'Accounting-Record-Number', RN}]};
+%% send_protocol_error
+request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4},
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}) ->
+ Ans = ['answer-message', {'Result-Code', ?TOO_BUSY},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR}],
+ {reply, Ans};
+
request(#diameter_base_ASR{'Session-Id' = SId,
'AVP' = Avps},
#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}};
+ {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;
+%% 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};
+%% send_destination_6
request(#diameter_base_STR{'Destination-Host'= [H]},
#diameter_caps{origin_host = {OH, _}})
when H /= OH ->
@@ -822,11 +1011,12 @@ request(#diameter_base_STR{'Destination-Host'= [H]},
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}]};
+%% send_error
request(#diameter_base_RAR{}, _Caps) ->
receive after 2000 -> ok end,
{protocol_error, ?TOO_BUSY}.
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index 5898e125ae..80b1769d04 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -40,7 +40,8 @@ MODULES = \
diameter_relay_SUITE \
diameter_tls_SUITE \
diameter_failover_SUITE \
- diameter_dpr_SUITE
+ diameter_dpr_SUITE \
+ diameter_event_SUITE
HRL_FILES = \
diameter_ct.hrl
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index c9f74ffcec..7b2208137b 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.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
@@ -18,7 +18,7 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.3
+DIAMETER_VSN = 1.3.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)"