diff options
Diffstat (limited to 'lib/diameter')
-rw-r--r-- | lib/diameter/doc/src/Makefile | 4 | ||||
-rw-r--r-- | lib/diameter/doc/src/notes.xml | 16 | ||||
-rw-r--r-- | lib/diameter/src/Makefile | 10 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter.appup.src | 29 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_codec.erl | 8 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_peer_fsm.erl | 5 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_service.erl | 31 | ||||
-rw-r--r-- | lib/diameter/test/diameter_event_SUITE.erl | 182 | ||||
-rw-r--r-- | lib/diameter/test/diameter_traffic_SUITE.erl | 258 | ||||
-rw-r--r-- | lib/diameter/test/modules.mk | 3 | ||||
-rw-r--r-- | lib/diameter/vsn.mk | 4 |
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)" |