diff options
Diffstat (limited to 'lib/diameter')
21 files changed, 582 insertions, 272 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/diameter.xml b/lib/diameter/doc/src/diameter.xml index b7669b760b..7e50f338d3 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -69,8 +69,8 @@ Incoming Diameter requests are communicated as callbacks to a specified in the service configuration.</p> <p> -Beware the difference between <em>diameter</em> (not capitalised) and -<em>Diameter</em> (capitalised). +Beware the difference between <em>diameter</em> (not capitalized) and +<em>Diameter</em> (capitalized). The former refers to the Erlang application named diameter whose main api is defined here, the latter to Diameter protocol in the sense of &the_rfc;.</p> @@ -488,16 +488,23 @@ candidates list.</p> <marker id="service_event"/> </item> - -<tag><c>service_event() = #diameter_event{}</c></tag> +<tag><c>service_event() = #diameter_event{service = &service_name;, + info = &service_event_info;}</c></tag> <item> <p> An event message sent to processes that have subscribed to these using &subscribe;.</p> +<marker id="service_event_info"/> +</item> + +<tag><c>service_event_info() = term()</c></tag> + +<item> + <p> -The <c>info</c> field of the event record can have one of the -following types.</p> +The <c>info</c> field of a &service_event; record. +Can have one of the following types.</p> <taglist> @@ -534,9 +541,9 @@ Otherwise a connection has reestablished without the loss or connectivity.</p> <p> -Note that a single <c>up</c>/<c>down</c> event for a given peer -corresponds to one &app_peer_up;/&app_peer_down; -callback for each of the Diameter applications negotiated during +Note that a single <c>up</c> or <c>down</c> event for a given peer +corresponds to multiple &app_peer_up; or &app_peer_down; +callbacks, one for each of the Diameter applications negotiated during capablilities exchange. That is, the event communicates connectivity with the peer as a whole while the callbacks communicate connectivity with @@ -582,7 +589,7 @@ CB = &evaluable; <p> An incoming CER has been answered with the indicated result code or discarded. -<c>Caps</c> contains pairs of values for the the local node and remote +<c>Caps</c> contains pairs of values for the local node and remote peer. <c>Pkt</c> contains the CER in question. In the case of rejection by a capabilities callback, the tuple @@ -600,7 +607,7 @@ Pkt = #diameter_packet{} <p> An incoming CER contained errors and has been answered with the indicated result code. -<c>Caps</c> contains only values for the the local node. +<c>Caps</c> contains only values for the local node. <c>Pkt</c> contains the CER in question.</p> </item> @@ -624,7 +631,7 @@ ResultCode = integer() An incoming CEA has been rejected for the indicated reason. An integer-valued <c>Result</c> indicates the result code sent by the peer. -<c>Caps</c> contains pairs of values for the the local node and remote +<c>Caps</c> contains pairs of values for the local node and remote peer. <c>Pkt</c> contains the CEA in question. In the case of rejection by a capabilities callback, the tuple @@ -640,7 +647,7 @@ Pkt = #diameter_packet{} <p> An incoming CEA contained errors and has been rejected. -<c>Caps</c> contains only values for the the local node. +<c>Caps</c> contains only values for the local node. <c>Pkt</c> contains the CEA in question.</p> </item> @@ -667,11 +674,14 @@ Config = {connect|listen, [transport_opt()]} An RFC 3539 watchdog state machine has changed state.</p> </item> -</taglist> - +<tag><c>any()</c></tag> +<item> <p> For forward compatibility, a subscriber should be prepared to receive info fields of forms other than the above.</p> +</item> + +</taglist> <marker id="service_name"/> </item> @@ -709,6 +719,15 @@ passed to &call;, while for an incoming request the application identifier in the message header determines the application, the identifier being specified in the application's &dictionary; file.</p> + +<warning> +<p> +The capabilities advertised by a node must match its configured +applications. In particular, <c>application</c> configuration must +be matched by corresponding &capability; configuration, of +Application-Id AVP's in particular.</p> +</warning> + </item> <tag><c>{restrict_connections, false @@ -787,6 +806,16 @@ The list of Diameter applications to which the transport should be restricted. Defaults to all applications configured on the service in question. Applications not configured on the service in question are ignored.</p> + +<warning> +<p> +The capabilities advertised by a node must match its configured +applications. +In particular, setting <c>applications</c> on a transport typically +implies having to set matching Application-Id AVP's in a +&capabilities; tuple.</p> +</warning> + </item> <marker id="capabilities"/> @@ -858,9 +887,8 @@ case the corresponding callbacks are applied until either all return The number of milliseconds after which a transport process having an established transport connection will be terminated if the expected capabilities exchange message (CER or CEA) is not received from the peer. -For a connecting transport, the timing reconnection attempts is -governed by &watchdog_timer; or -&reconnect_timer; expiry. +For a connecting transport, the timing of reconnection attempts is +governed by &watchdog_timer; or &reconnect_timer; expiry. For a listening transport, the peer determines the timing.</p> <p> @@ -877,7 +905,7 @@ transport connection having watchdog state <c>OKAY</c>. Applied to <c>Reason=transport|service|application</c> and the <c>&transport_ref;</c> and <c>&app_peer;</c> -in question, <c>Reason</c> indicating whether the the diameter +in question, <c>Reason</c> indicating whether the diameter application is being stopped, the service in question is being stopped at &stop_service; or the transport in question is being removed at &remove_transport;, @@ -1733,7 +1761,7 @@ a service.</p> It is not an error to subscribe to events from a service that does not yet exist. Doing so before adding transports is required to guarantee the -reception of all related events.</p> +reception of all transport-related events.</p> </desc> </func> diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml index e3b8c733b7..fe2389d57d 100644 --- a/lib/diameter/doc/src/diameter_tcp.xml +++ b/lib/diameter/doc/src/diameter_tcp.xml @@ -26,7 +26,7 @@ <erlref> <header> <copyright> -<year>2011</year><year>2012</year> +<year>2011</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -65,9 +65,8 @@ It can be specified as the value of a <c>transport_module</c> option to &mod_add_transport; and implements the behaviour documented in &man_transport;. -TLS security is supported, both as an upgrade following -capabilities exchange as specified by &the_rfc; and -at connection establishment as in the current draft standard.</p> +TLS security is supported, either as an upgrade following +capabilities exchange or at connection establishment.</p> <p> Note that the ssl application is required for TLS and must be started diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml index d448b01eba..1258e8e85d 100644 --- a/lib/diameter/doc/src/notes.xml +++ b/lib/diameter/doc/src/notes.xml @@ -11,7 +11,7 @@ <header> <copyright> <year>2011</year> -<year>2012</year> +<year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -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/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent index 4647c42f85..9945bcadd3 100644 --- a/lib/diameter/doc/src/seealso.ent +++ b/lib/diameter/doc/src/seealso.ent @@ -54,6 +54,7 @@ significant. <!ENTITY mod_evaluable '<seealso marker="diameter#evaluable">diameter:evaluable()</seealso>'> <!ENTITY mod_peer_filter '<seealso marker="diameter#peer_filter">diameter:peer_filter()</seealso>'> <!ENTITY mod_service_event '<seealso marker="diameter#service_event">diameter:service_event()</seealso>'> +<!ENTITY mod_service_event_info '<seealso marker="diameter#service_event_info">diameter:service_event_info()</seealso>'> <!ENTITY mod_service_name '<seealso marker="diameter#service_name">diameter:service_name()</seealso>'> <!ENTITY mod_service_opt '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso>'> <!ENTITY mod_transport_opt '<seealso marker="diameter#transport_opt">diameter:transport_opt()</seealso>'> diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index 060659bce9..a08c204a23 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -104,7 +104,7 @@ endif ERL_COMPILE_FLAGS += \ +'{parse_transform,sys_pre_attributes}' \ - +'{attribute,insert,app_vsn,$(APP_VSN)}' \ + +'{attribute,insert,app_vsn,"$(APP_VSN)"}' \ +warn_export_vars \ +warn_unused_vars \ -pa $(ABS_EBIN) \ @@ -119,26 +119,34 @@ 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) +# Build unofficial patches with some degree of traceability. Refuse to +# build if there are diffs from HEAD since that defeats the purpose. +patch: + git diff --exit-code HEAD + $(MAKE) opt PRE_VSN="-$(shell git rev-list --max-count=1 HEAD | cut -c 1-8)" + debug: @$(MAKE) TYPE=debug opt # 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 ' ' ,`; \ + R=`echo $(REGISTERED) | tr ' ' ,`; \ sed -e 's;%VSN%;$(VSN);' \ -e "s;%MODULES%;$$M;" \ + -e "s;%REGISTERED%;$$R;" \ $< > $@ $(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 +262,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) \ > $@ @@ -265,7 +273,7 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile .PHONY: debug opt release_docs_spec release_spec .PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%) .PHONY: $(EXAMPLE_DIRS:%/=release_examples_%) -.PHONY: plt dialyze +.PHONY: plt dialyze patch # Keep intermediate files. .SECONDARY: $(DICT_ERLS) $(DICT_HRLS) gen/$(DICT_YRL:%=%.erl) diff --git a/lib/diameter/src/base/diameter.app.src b/lib/diameter/src/base/diameter.app.src index c092fdb022..ceefb9b398 100644 --- a/lib/diameter/src/base/diameter.app.src +++ b/lib/diameter/src/base/diameter.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,7 +21,7 @@ [{description, "Diameter protocol"}, {vsn, "%VSN%"}, {modules, [%MODULES%]}, - {registered, []}, + {registered, [%REGISTERED%]}, {applications, [stdlib, kernel]}, {env, []}, {mod, {diameter_app, []}} diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src index 5655f98c1b..f6d772b534 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,10 @@ {"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", [{restart_application, diameter}]}, + {"1.3.1", [{restart_application, diameter}]} ], [ {"0.9", [{restart_application, diameter}]}, @@ -52,6 +35,8 @@ {"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", [{restart_application, diameter}]}, + {"1.3.1", [{restart_application, diameter}]} ] }. 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.erl b/lib/diameter/src/base/diameter_peer.erl index 1b2f32ddff..25c9eab4cb 100644 --- a/lib/diameter/src/base/diameter_peer.erl +++ b/lib/diameter/src/base/diameter_peer.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 @@ -33,9 +33,6 @@ abort/1, notify/2]). -%% Old interface only called from old code. --export([start/3]). %% < diameter-1.2 (R15B02) - %% Server start. -export([start_link/0]). @@ -73,14 +70,6 @@ notify(SvcName, T) -> rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}). %%% --------------------------------------------------------------------------- -%%% # start/3 -%%% --------------------------------------------------------------------------- - -%% From old code: make it restart. -start(_T, _Opts, #diameter_service{}) -> - {error, restart}. - -%%% --------------------------------------------------------------------------- %%% # start/1 %%% --------------------------------------------------------------------------- diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index c4320fcb99..5dab6214b1 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.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 @@ -95,10 +95,8 @@ -record(state, {state %% of RFC 3588 Peer State Machine - :: 'Wait-Conn-Ack' %% old code - | {'Wait-Conn-Ack', uint32()} + :: {'Wait-Conn-Ack', uint32()} | recv_CER - | 'Wait-CEA' %% old code | {'Wait-CEA', uint32(), uint32()} | 'Open', mode :: accept | connect | {connect, reference()}, @@ -142,8 +140,7 @@ %%% Output: Pid %%% --------------------------------------------------------------------------- --spec start(T, [Opt], #diameter_service{} %% from old code - | {diameter:sequence(), +-spec start(T, [Opt], {diameter:sequence(), diameter:restriction(), #diameter_service{}}) -> pid() @@ -175,9 +172,6 @@ init(T) -> proc_lib:init_ack({ok, self()}), gen_server:enter_loop(?MODULE, [], i(T)). -i({WPid, Type, Opts, #diameter_service{} = Svc}) -> %% from old code - i({WPid, Type, Opts, {?NOMASK, [node() | nodes()], Svc}}); - i({WPid, T, Opts, {Mask, Nodes, #diameter_service{applications = Apps, capabilities = LCaps} = Svc}}) -> @@ -334,10 +328,6 @@ eraser(Key) -> %% transition/2 -%% Started in old code. -transition(T, #state{state = 'Wait-Conn-Ack' = PS} = S) -> - transition(T, S#state{state = {PS, ?EVENT_TIMEOUT}}); - %% Connection to peer. transition({diameter, {TPid, connected, Remote}}, #state{transport = TPid, @@ -388,8 +378,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, _}, _) -> @@ -400,10 +391,6 @@ transition({send, Msg}, #state{transport = TPid}) -> send(TPid, Msg), ok; -%% Messages from old (diameter_service) code. -transition(shutdown = T, #state{parent = Pid} = S) -> - transition({T, Pid, service}, S); %% Reason irrelevant: old code has no cb - %% Request for graceful shutdown at remove_transport, stop_service of %% application shutdown. transition({shutdown = T, Pid}, S) -> @@ -507,22 +494,13 @@ build_CER(#state{service = #diameter_service{capabilities = LCaps}}) -> %% encode/1 encode(Rec) -> - Seq = diameter_session:sequence(sequence()), + Seq = diameter_session:sequence({_,_} = getr(?SEQUENCE_KEY)), Hdr = #diameter_header{version = ?DIAMETER_VERSION, end_to_end_id = Seq, hop_by_hop_id = Seq}, diameter_codec:encode(?BASE, #diameter_packet{header = Hdr, msg = Rec}). -sequence() -> - case getr(?SEQUENCE_KEY) of - {_,_} = Mask -> - Mask; - undefined -> %% started in old code - putr(?SEQUENCE_KEY, ?NOMASK), - ?NOMASK - end. - %% recv/2 %% RFC 3588 has result code 5015 for an invalid length but if a @@ -584,10 +562,8 @@ rcv('CEA', #diameter_packet{header = #diameter_header{end_to_end_id = Eid, hop_by_hop_id = Hid}} = Pkt, - #state{state = {'Wait-CEA' = T, Hid, Eid}} + #state{state = {'Wait-CEA', Hid, Eid}} = S) -> - handle_CEA(Pkt, S#state{state = T}); -rcv('CEA', Pkt, #state{state = 'Wait-CEA'} = S) -> %% old code handle_CEA(Pkt, S); %% Incoming CER @@ -1020,14 +996,10 @@ dpr(Reason, #state{state = 'Open', dpr = false, service = #diameter_service{capabilities = Caps}} = S) -> - case getr(?DPR_KEY) of - CBs when is_list(CBs) -> - Ref = getr(?REF_KEY), - Peer = {self(), Caps}, - dpr(CBs, [Reason, Ref, Peer], S); - undefined -> %% started in old code - send_dpr(Reason, [], S) - end; + CBs = getr(?DPR_KEY), + Ref = getr(?REF_KEY), + Peer = {self(), Caps}, + dpr(CBs, [Reason, Ref, Peer], S); %% Connection is open, DPR already sent. dpr(_, #state{state = 'Open'}) -> diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 91384b8b91..d2a416166f 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 @@ -361,9 +361,6 @@ find_state(SvcName) -> fs([#state{} = S]) -> S; -fs([S]) -> %% inserted from old code - upgrade(S); - fs([]) -> false. @@ -462,10 +459,6 @@ i(_, false) -> %%% # handle_call(Req, From, State) %%% --------------------------------------------------------------------------- -handle_call(T, From, S) - when not is_record(S, state) -> - handle_call(T, From, upgrade(S)); - handle_call(state, _, S) -> {reply, S, S}; @@ -489,10 +482,6 @@ handle_call({pick_peer, Local, Remote, App}, _From, S) -> handle_call({call_module, AppMod, Req}, From, S) -> call_module(AppMod, Req, From, S); -%% Call from old code. -handle_call({info, Item}, _From, S) -> - {reply, service_info(Item, S), S}; - handle_call(stop, _From, S) -> shutdown(service, S), {stop, normal, ok, S}; @@ -500,14 +489,6 @@ handle_call(stop, _From, S) -> %% gets the reply. We deal with this in the call to the server, %% stating a monitor that waits for DOWN before returning. -%% Watchdog is asking for the sequence mask. -handle_call(sequence, _From, #state{options = [{_, Mask} | _]} = S) -> - {reply, Mask, S}; - -%% Watchdog is asking for the nodes restriction. -handle_call(restriction, _From, #state{options = [_,_,_,{_,R} | _]} = S) -> - {reply, R, S}; - handle_call(Req, From, S) -> unexpected(handle_call, [Req, From], S), {reply, nok, S}. @@ -530,10 +511,7 @@ handle_info(T, #state{} = S) -> {noreply, S}; {stop, Reason} -> {stop, {shutdown, Reason}, S} - end; - -handle_info(T, S) -> - handle_info(T, upgrade(S)). + end. %% transition/2 @@ -576,9 +554,9 @@ transition({reconnect, Pid}, S) -> ok; %% Watchdog is sending notification of a state transition. Note that -%% the connection_up/down messages are pre-date this message and are -%% still used. A watchdog message will follow these and communicate -%% the same state as was set in handling connection_up/down. +%% the connection_up/down messages pre-date this message and are still +%% used. A watchdog message will follow these and communicate the same +%% state as was set in handling connection_up/down. transition({watchdog, Pid, {TPid, From, To}}, #state{service_name = SvcName, peerT = PeerT}) -> #peer{ref = Ref, type = T, options = Opts, op_state = {OS,_}} @@ -643,39 +621,10 @@ transition({failover, TRef, Seqs}, S) -> failover(TRef, Seqs, S), ok; -%% Ensure upgraded state is stored in state table. -transition(upgrade, _) -> - ok; - transition(Req, S) -> unexpected(handle_info, [Req], S), ok. -%% upgrade/1 - -upgrade({state, Id, Svc, Name, Svc, PT, CT, SB, UB, SD, LD, MPid}) -> - S = #state{id = Id, - service_name = Name, - service = Svc, - peerT = PT, - connT = CT, - shared_peers = SD, - local_peers = LD, - monitor = MPid, - options = [{sequence, ?NOMASK}, - {share_peers, SB}, - {use_shared_peers, UB}, - {restrict_connections, ?RESTRICT}]}, - upgrade_insert(S), - S. - -upgrade_insert(#state{service = #diameter_service{pid = Pid}} = S) -> - if Pid == self() -> - ets:insert(?STATE_TABLE, S); - true -> - Pid ! upgrade - end. - %%% --------------------------------------------------------------------------- %%% # terminate(Reason, State) %%% --------------------------------------------------------------------------- @@ -859,10 +808,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}, @@ -944,6 +893,7 @@ type(connect = T) -> T. start(Ref, Type, Opts, #state{peerT = PeerT, connT = ConnT, + options = SvcOpts, service_name = SvcName, service = Svc}) when Type == connect; @@ -951,6 +901,7 @@ start(Ref, Type, Opts, #state{peerT = PeerT, Pid = s(Type, Ref, {ConnT, Opts, SvcName, + SvcOpts, merge_service(Opts, Svc)}), insert(PeerT, #peer{pid = Pid, type = Type, @@ -963,13 +914,8 @@ start(Ref, Type, Opts, #state{peerT = PeerT, %% record is what is passed back into application callbacks. s(Type, Ref, T) -> - case diameter_watchdog:start({Type, Ref}, T) of - {_MRef, Pid} -> - Pid; - Pid when is_pid(Pid) -> %% from old code - erlang:monitor(process, Pid), - Pid - end. + {_MRef, Pid} = diameter_watchdog:start({Type, Ref}, T), + Pid. %% merge_service/2 @@ -1116,12 +1062,7 @@ init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) -> %% find_app/2 find_app(Alias, Apps) -> - case lists:keyfind(Alias, #diameter_app.alias, Apps) of - #diameter_app{options = E} = A when is_atom(E) -> %% upgrade - A#diameter_app{options = [{answer_errors, E}]}; - A -> - A - end. + lists:keyfind(Alias, #diameter_app.alias, Apps). %% Don't bring down the service (and all associated connections) %% regardless of what happens. @@ -1457,7 +1398,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 +1406,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), @@ -1818,9 +1760,6 @@ request_peer_down(TPid, S) -> %%% recv_request/3 %%% --------------------------------------------------------------------------- -recv_request(TPid, Pkt, {ConnT, SvcName, Apps}) -> %% upgrade - recv_request(TPid, Pkt, {ConnT, SvcName, Apps, ?NOMASK}); - recv_request(TPid, Pkt, {ConnT, SvcName, Apps, Mask}) -> try ets:lookup(ConnT, TPid) of [C] -> @@ -2053,15 +1992,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 +2119,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 +2188,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/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index 243ad0a986..a5429c967c 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.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 @@ -68,11 +68,12 @@ %% that a failed capabilities exchange produces the desired exit %% reason. --spec start(Type, {RecvData, [Opt], SvcName, #diameter_service{}}) +-spec start(Type, {RecvData, [Opt], SvcName, SvcOpts, #diameter_service{}}) -> {reference(), pid()} when Type :: {connect|accept, diameter:transport_ref()}, RecvData :: term(), Opt :: diameter:transport_opt(), + SvcOpts :: [diameter:service_opt()], SvcName :: diameter:service_name(). start({_,_} = Type, T) -> @@ -107,23 +108,20 @@ i({Ref, {_, Pid, _} = T}) -> make_state(T); {'DOWN', MRef, process, _, _} = D -> exit({shutdown, D}) - end; - -i({_, Pid, _} = T) -> %% from old code - erlang:monitor(process, Pid), - make_state(T). + end. make_state({T, Pid, {RecvData, Opts, SvcName, + SvcOpts, #diameter_service{applications = Apps, capabilities = Caps} = Svc}}) -> random:seed(now()), putr(restart, {T, Opts, Svc}), %% save seeing it in trace putr(dwr, dwr(Caps)), %% - {_,_} = Mask = call(Pid, sequence), - Restrict = call(Pid, restriction), + {_,_} = Mask = proplists:get_value(sequence, SvcOpts), + Restrict = proplists:get_value(restrict_connections, SvcOpts), Nodes = restrict_nodes(Restrict), #watchdog{parent = Pid, transport = monitor(diameter_peer_fsm:start(T, @@ -136,10 +134,6 @@ make_state({T, Pid, {RecvData, sequence = Mask, restrict = {Restrict, lists:member(node(), Nodes)}}. -%% Retrieve the sequence mask from the parent from the parent, rather -%% than having it passed into init/1, for upgrade reasons: the call to -%% diameter_service:receive_message/3 passes back the mask. - %% handle_call/3 handle_call(_, _, State) -> @@ -342,15 +336,6 @@ transition({state, Pid}, #watchdog{status = S}) -> %% =========================================================================== -%% Only call "upwards", to the parent service. -call(Pid, Req) -> - try - gen_server:call(Pid, Req, infinity) - catch - exit: Reason -> - exit({shutdown, {Req, Reason}}) - end. - monitor(Pid) -> erlang:monitor(process, Pid), Pid. diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index 01f9284881..25207625be 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -103,3 +103,12 @@ EXAMPLES = \ dict/rfc4072_eap.dia \ dict/rfc4590_digest.dia \ dict/rfc4740_sip.dia + +# Registered server names. + +REGISTERED = \ + diameter_config \ + diameter_peer \ + diameter_reg \ + diameter_stats \ + diameter_sync diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 79b8b851fb..3cb13d7043 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.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 @@ -188,8 +188,6 @@ i({connect, Pid, Opts, Addrs, Ref}) -> #transport{parent = Pid, mode = {connect, connect(Sock, RAs, RP, [])}, socket = Sock}; -i({connect, _, _, _} = T) -> %% from old code - x(T); %% An accepting transport spawned by diameter. i({accept, Pid, LPid, Sock, Ref}) @@ -201,8 +199,6 @@ i({accept, Pid, LPid, Sock, Ref}) #transport{parent = Pid, mode = {accept, LPid}, socket = Sock}; -i({accept, _, _, _} = T) -> %% from old code - x(T); %% An accepting transport spawned at association establishment. i({accept, Ref, LPid, Sock, Id}) -> diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index f3fbbee609..7ec7b1c5e7 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.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 @@ -80,7 +80,7 @@ %% Accepting/connecting transport process state. -record(transport, - {socket :: inet:socket() | ssl:sslsock(), %% accept or connect socket + {socket :: inet:socket() | ssl:sslsocket(), %% accept/connect socket parent :: pid(), %% of process that started us module :: module(), %% gen_tcp-like module frag = <<>> :: binary() | {tref(), frag()}, %% message fragment diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile index 866d135bd9..aa4b7eaeb1 100644 --- a/lib/diameter/test/Makefile +++ b/lib/diameter/test/Makefile @@ -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 @@ -62,17 +62,15 @@ ERL_COMPILE_FLAGS += +warn_export_vars \ # Targets # ---------------------------------------------------- +all debug opt: $(TARGET_FILES) + # Require success ... -all: opt +run: $(SUITES) # ... or not. any: opt $(MAKE) -i $(SUITES) -run: $(SUITES) - -debug opt: $(TARGET_FILES) - clean: rm -f $(TARGET_FILES) rm -f depend.mk @@ -101,7 +99,10 @@ help: @echo " Compile all test suites." @echo @echo " run:" - @echo " Compile and run all test suites." + @echo " Compile and run all test suites, stop on failure." + @echo + @echo " any:" + @echo " Compile and run all test suites, ignore any failures." @echo @echo " $(SUITES):" @echo " Compile and run a specific test suite." 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..74f4c57b70 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,8 +18,5 @@ # %CopyrightEnd% APPLICATION = diameter -DIAMETER_VSN = 1.3 -PRE_VSN = -APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)" - -TICKETS = +DIAMETER_VSN = 1.4 +APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN) |