aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter')
-rw-r--r--lib/diameter/doc/src/diameter.xml103
-rw-r--r--lib/diameter/doc/src/diameter_app.xml34
-rw-r--r--lib/diameter/doc/src/diameter_compile.xml32
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml5
-rw-r--r--lib/diameter/doc/src/diameter_make.xml38
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml20
-rw-r--r--lib/diameter/doc/src/diameter_transport.xml30
-rw-r--r--lib/diameter/doc/src/notes.xml18
-rw-r--r--lib/diameter/examples/code/peer.erl28
-rw-r--r--lib/diameter/examples/code/redirect_cb.erl4
-rw-r--r--lib/diameter/src/Makefile2
-rw-r--r--lib/diameter/src/base/diameter.erl12
-rw-r--r--lib/diameter/src/base/diameter_config.erl142
-rw-r--r--lib/diameter/src/base/diameter_lib.erl245
-rw-r--r--lib/diameter/src/base/diameter_peer.erl16
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl42
-rw-r--r--lib/diameter/src/base/diameter_service.erl114
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl16
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl7
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl79
-rw-r--r--lib/diameter/test/Makefile5
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_codec_test.erl6
-rw-r--r--lib/diameter/test/diameter_compiler_SUITE.erl51
-rw-r--r--lib/diameter/test/diameter_config_SUITE.erl272
-rw-r--r--lib/diameter/test/diameter_ct.erl2
-rw-r--r--lib/diameter/test/diameter_distribution_SUITE.erl372
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_examples_SUITE.erl334
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_transport_SUITE.erl122
-rw-r--r--lib/diameter/test/diameter_util.erl26
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl2
-rw-r--r--lib/diameter/test/modules.mk3
34 files changed, 1738 insertions, 450 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 8e9ec06ff9..318c98f786 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY nodes
+ '<seealso marker="erts:erlang#nodes-0">erlang:nodes/0</seealso>'>
<!ENTITY make_ref
'<seealso marker="erts:erlang#make_ref-0">erlang:make_ref/0</seealso>'>
<!ENTITY transport_module
@@ -41,7 +43,7 @@ under the License.
<approved></approved>
<checked></checked>
<date></date>
-<rev>%VSN%</rev>
+<rev></rev>
<file>diameter.xml</file>
</header>
@@ -341,8 +343,9 @@ Has one of the following types.</p>
An address list is available to the start function of a
&transport_module;, which
can return a new list for use in the subsequent CER or CEA.
-Host-IP-Address need not be specified if the transport start function
-returns an address list.</p>
+Host-IP-Address need not be specified if the transport module in
+question communicates an address list as described in
+&man_transport;</p>
</item>
<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag>
@@ -772,16 +775,16 @@ Application-Id AVP's in particular.</p>
| evaluable()}</c></tag>
<item>
<p>
-Specifies the degree to which multiple transport connections to the
-same peer are accepted by the service.</p>
+Specifies the degree to which the service allows multiple transport
+connections to the same peer.</p>
<p>
If type <c>[node()]</c> then a connection is rejected if another already
exists on any of the specified nodes.
-Values of type <c>false</c>, <c>node</c>, <c>nodes</c> or
+Types <c>false</c>, <c>node</c>, <c>nodes</c> and
&evaluable; are equivalent to
-values <c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the
-evaluated value, respectively, evaluation of each expression taking
+<c>[]</c>, <c>[node()]</c>, <c>[node()|nodes()]</c> and the
+evaluated value respectively, evaluation of each expression taking
place whenever a new connection is to be established.
Note that <c>false</c> allows an unlimited number of connections to be
established with the same peer.</p>
@@ -819,6 +822,88 @@ non-negative integer less than <c>1 bsl (32-N)</c>.</p>
<p>
Defaults to <c>{0,32}</c>.</p>
+
+<warning>
+<p>
+Multiple Erlang nodes implementing the same Diameter node should
+be configured with different sequence masks to ensure that each node
+uses a unique range of End-to-End and Hop-by-Hop identifiers for
+outgoing requests.</p>
+</warning>
+</item>
+
+<tag><c>{share_peers, boolean() | [node()] | evaluable()}</c></tag>
+<item>
+<p>
+Specifies nodes to which peer connections established on the local
+Erlang node are communicated.
+Shared peers become available in the remote candidates list passed to
+&app_pick_peer; callbacks on remote nodes whose services are
+configured to use them: see <c>use_shared_peers</c> below.</p>
+
+<p>
+If <c>false</c> then peers are not shared.
+If <c>[node()]</c> then peers are shared with the specified list of
+nodes.
+If <c>evaluable()</c> then peers are shared with the nodes returned
+by the specified function, evaluated whenever a peer connection
+becomes available or a remote service requests information about local
+connections.
+The value <c>true</c> is equivalent to <c>fun &nodes;</c>.
+The value <c>node()</c> in a node list is ignored, so a collection of
+services can all be configured to share with the same list of
+nodes.</p>
+
+<p>
+Defaults to <c>false</c>.</p>
+
+<note>
+<p>
+Peers are only shared with services of the same name for the purpose
+of sending outgoing requests.
+Since the value of the &application_opt; <c>alias</c>, passed to
+&call;, is the handle for identifying a peer as a suitable
+candidate, services that share peers must use the same aliases to
+identify their supported applications.
+They should typically also configure identical &capabilities;, since
+by sharing peer connections they are distributing the implementation
+of a single Diameter node across multiple Erlang nodes.</p>
+</note>
+</item>
+
+<tag><c>{use_shared_peers, boolean() | [node()] | evaluable()}</c></tag>
+<item>
+<p>
+Specifies nodes from which communicated peers are made available in
+the remote candidates list of &app_pick_peer; callbacks.</p>
+
+<p>
+If <c>false</c> then remote peers are not used.
+If <c>[node()]</c> then only peers from the specified list of nodes
+are used.
+If <c>evaluable()</c> then only peers returned by the specified
+function are used, evaluated whenever a remote service communicates
+information about an available peer connection.
+The value <c>true</c> is equivalent to <c>fun &nodes;</c>.
+The value <c>node()</c> in a node list is ignored.</p>
+
+<p>
+Defaults to <c>false</c>.</p>
+
+<note>
+<p>
+A service that does not use shared peers will always pass the empty
+list as the second argument of &app_pick_peer; callbacks.</p>
+</note>
+
+<warning>
+<p>
+Sending a request over a peer connection on a remote node is less
+efficient than sending it over a local connection.
+It may be preferable to make use of the &service_opt;
+<c>restrict_connections</c> and maintain a dedicated connection on
+each node from which requests are sent.</p>
+</warning>
</item>
</taglist>
@@ -1078,7 +1163,7 @@ transport.</p>
<marker id="transport_config"/>
<tag><c>{transport_config, term()}</c></tag>
-<tag><c>{transport_config, term(), &dict_Unsigned32;}</c></tag>
+<tag><c>{transport_config, term(), &dict_Unsigned32; | infinity}</c></tag>
<item>
<p>
A term passed as the third argument to the &transport_start; function of
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index d0f1b22ebd..d4fb792787 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -37,7 +37,7 @@ under the License.
<approved></approved>
<checked></checked>
<date></date>
-<rev>%REV%</rev>
+<rev></rev>
<file>diameter_app.xml</file>
</header>
@@ -196,7 +196,8 @@ process.</p>
</type>
<desc>
<p>
-Invoked to signal the availability of a peer connection.
+Invoked to signal the availability of a peer connection on the local
+Erlang node.
In particular, capabilities exchange with the peer has indicated
support for the application in question, the RFC 3539 watchdog state
machine for the connection has reached state <c>OKAY</c> and Diameter
@@ -230,8 +231,8 @@ handled independently of &peer_up; and &peer_down;.</p>
</type>
<desc>
<p>
-Invoked to signal that a peer connection is no longer available
-following a previous call to &peer_up;.
+Invoked to signal that a peer connection on the local Erlang node is
+no longer available following a previous call to &peer_up;.
In particular, that the RFC 3539 watchdog state machine for the
connection has left state <c>OKAY</c> and the peer will no longer be a
candidate in &pick_peer; callbacks.</p>
@@ -240,11 +241,11 @@ candidate in &pick_peer; callbacks.</p>
</func>
<func>
-<name>Mod:pick_peer(Candidates, _Reserved, SvcName, State)
+<name>Mod:pick_peer(LocalCandidates, RemoteCandidates, SvcName, State)
-> Selection | false</name>
<fsummary>Select a target peer for an outgoing request.</fsummary>
<type>
-<v>Candidates = [&peer;]</v>
+<v>LocalCandidates = RemoteCandidates = [&peer;]</v>
<v>SvcName = &mod_service_name;</v>
<v>State = NewState = &state;</v>
<v>Selection = {ok, Peer} | {Peer, NewState}</v>
@@ -257,7 +258,7 @@ peer for an outgoing request.
The return value indicates the selected peer.</p>
<p>
-The candidate list contains only those peers that have advertised
+The candidate lists contain only those peers that have advertised
support for the Diameter application in question during capabilities
exchange, that have not be excluded by a <c>filter</c> option in
the call to &mod_call;
@@ -266,7 +267,11 @@ The order of the elements is unspecified except that any
peers whose Origin-Host and Origin-Realm matches that of the
outgoing request (in the sense of a <c>{filter, {all, [host, realm]}}</c>
option to &mod_call;)
-will be placed at the head of the list.</p>
+will be placed at the head of the list.
+<c>LocalCandidates</c> contains peers whose transport process resides
+on the local Erlang node while
+<c>RemoteCandidates</c> contains peers that have been communicated
+from other nodes by services of the same name.</p>
<p>
A callback that returns a peer() will be followed by a
@@ -286,16 +291,19 @@ retransmission to an alternate peer is abandoned if an answer is
received from a previously selected peer.</p>
<p>
-Returning <c>false</c> or <c>{false, NewState}</c> causes <c>{error,
-no_connection}</c> to be returned from &mod_call;.</p>
-
-<p>
The return values <c>false</c> and <c>{false, State}</c> (that is,
<c>NewState = State</c>) are equivalent, as are <c>{ok, Peer}</c> and
<c>{Peer, State}</c>.</p>
<note>
<p>
+The &mod_service_opt; <c>use_shared_peers</c> determines whether or
+not a service uses peers shared from other nodes.
+If not then <c>RemoteCandidates</c> is the empty list.</p>
+</note>
+
+<warning>
+<p>
The return value <c>{Peer, NewState}</c> is only allowed if
the Diameter application in question was configured with the
&mod_application_opt; <c>{call_mutates_state, true}</c>.
@@ -303,7 +311,7 @@ Otherwise, the <c>State</c> argument is always
the intial value as configured on the application, not any subsequent
value returned by a &peer_up;
or &peer_down; callback.</p>
-</note>
+</warning>
</desc>
diff --git a/lib/diameter/doc/src/diameter_compile.xml b/lib/diameter/doc/src/diameter_compile.xml
index 0bd7ad1789..fc81e4efed 100644
--- a/lib/diameter/doc/src/diameter_compile.xml
+++ b/lib/diameter/doc/src/diameter_compile.xml
@@ -11,7 +11,7 @@
<comref>
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -59,6 +59,7 @@ The module &man_make; provides an alternate compilation interface.</p>
Compile a single dictionary file to Erlang source.
Valid options are as follows.</p>
+<taglist>
<tag><![CDATA[-i <dir>]]></tag>
<item>
<p>
@@ -71,7 +72,6 @@ dependency, not an erl/hrl dependency.</p>
Multiple <c>-i</c> options can be specified.</p>
</item>
-<taglist>
<tag><![CDATA[-o <dir>]]></tag>
<item>
<p>
@@ -90,18 +90,30 @@ Supress erl and hrl generation, respectively.</p>
<tag><![CDATA[--prefix <prefix>]]></tag>
<item>
<p>
-Set <c>&dict_name;</c> or <c>&dict_prefix;</c> to the specified
-string.
-Overrides any setting in the file itself.</p>
+Transform the input dictionary before compilation, setting
+<c>&dict_name;</c> or <c>&dict_prefix;</c> to the specified
+string.</p>
</item>
-<tag><![CDATA[--inherits <dict>]]></tag>
+<tag><![CDATA[--inherits <arg>]]></tag>
<item>
<p>
-Append &dict_inherits; of the specified module.
-Specifying <c>"-"</c> has the effect of discarding clearing any
-previous inherits, both in the dictionary file and on the options
-list.</p>
+Transform the input dictionary before compilation, appending
+<c>&dict_inherits;</c> of the specified string.</p>
+
+<p>
+Two forms of <c>--inherits</c> have special meaning:</p>
+
+<pre>
+--inherits -
+--inherits Prev/Mod
+</pre>
+
+<p>
+The first has the effect of clearing any previous inherits, the second
+of replacing a previous inherits of <c>Prev</c> to one of <c>Mod</c>.
+This allows the semantics of the input dictionary to be changed without
+modifying the file itself.</p>
<p>
Multiple <c>--inherits</c> options can be specified.</p>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index 1034781ff2..419dc143af 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -263,15 +263,14 @@ dictionary's definitions but the former makes for easier reuse.</p>
<p>
All dictionaries should typically inherit &the_rfc; AVPs from
-<c>diameter_gen_base_rfc3588</c>.</p>
+<c>diameter_gen_base_rfc6733</c>.</p>
<p>
Example:</p>
<pre>
-@inherits diameter_gen_base_rfc3588
+@inherits diameter_gen_base_rfc6733
</pre>
-
</item>
<marker id="avp_types"/>
diff --git a/lib/diameter/doc/src/diameter_make.xml b/lib/diameter/doc/src/diameter_make.xml
index da6124310e..ec71251be1 100644
--- a/lib/diameter/doc/src/diameter_make.xml
+++ b/lib/diameter/doc/src/diameter_make.xml
@@ -14,6 +14,7 @@
<header>
<copyright>
<year>2012</year>
+<year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -73,7 +74,7 @@ Compile a single dictionary file to Erlang source.
<taglist>
-<tag><c>{include, Dir::string()}</c></tag>
+<tag><c>{include, string()}</c></tag>
<item>
<p>
Prepend the specified directory to the code path.
@@ -85,7 +86,7 @@ dependency, not an erl/hrl dependency.</p>
Multiple <c>include</c> options can be specified.</p>
</item>
-<tag><c>{outdir, Dir::string()}</c></tag>
+<tag><c>{outdir, string()}</c></tag>
<item>
<p>
Write generated source to the specified directory.
@@ -95,18 +96,30 @@ Defaults to the current working directory.</p>
<tag><c>{name|prefix, string()}</c></tag>
<item>
<p>
-Set <c>&dict_name;</c> or <c>&dict_prefix;</c> to the specified
-string.
-Overrides any setting in the file itself.</p>
+Transform the input dictionary before compilation, setting
+<c>&dict_name;</c> or <c>&dict_prefix;</c> to the specified
+string.</p>
</item>
-<tag><c>{inherits, Mod::string()}</c></tag>
+<tag><c>{inherits, string()}</c></tag>
<item>
<p>
-Append &dict_inherits; of the specified module.
-Specifying <c>"-"</c> has the effect of discarding clearing any
-previous inherits, both in the dictionary file and on the options
-list.</p>
+Transform the input dictionary before compilation, appending
+<c>&dict_inherits;</c> of the specified string.</p>
+
+<p>
+Two forms of <c>@inherits</c> have special meaning:</p>
+
+<pre>
+{inherits, "-"}
+{inherits, "Prev/Mod"}
+</pre>
+
+<p>
+The first has the effect of clearing any previous inherits, the second
+of replacing a previous inherits of <c>Prev</c> to one of <c>Mod</c>.
+This allows the semantics of the input dictionary to be changed without
+modifying the file itself.</p>
<p>
Multiple <c>inherits</c> options can be specified.</p>
@@ -126,8 +139,9 @@ Multiple <c>inherits</c> options can be specified.</p>
<p>
All options are string-valued.
-In particular, it is not currently possible to
-an &dict_inherits; module as an atom() or a path as a &filename;</p>
+In particular, it is not currently possible to specify
+an &dict_inherits; module as an atom(), or a path as an arbitrary
+&filename;</p>
</section>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index 01c781d553..8e509aa829 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY start '<seealso marker="#start-3">start/3</seealso>'>
<!ENTITY gen_tcp_connect3
'<seealso marker="kernel:gen_tcp#connect-3">gen_tcp:connect/3</seealso>'>
<!ENTITY gen_tcp_listen2
@@ -81,7 +82,9 @@ before configuring TLS capability on diameter transports.</p>
<func>
<name>start({Type, Ref}, Svc, [Opt])
- -> {ok, Pid, [LAddr]} | {error, Reason}</name>
+ -> {ok, Pid}
+ | {ok, Pid, [LAddr]}
+ | {error, Reason}</name>
<fsummary>Start a transport process.</fsummary>
<type>
<v>Type = connect | accept</v>
@@ -153,13 +156,14 @@ that will not be forthcoming, which will eventually cause the RFC 3539
watchdog to take down the connection.</p>
<p>
-If the <c>#diameter_service{}</c> record has more than one
-<c>Host-IP-Address</c> and option <c>ip</c> is unspecified then the
-first of the these addresses is used as the local address.</p>
-
-<p>
-The returned local address list has length one.</p>
-
+If an <c>ip</c> option is not specified then the first element of a
+non-empty <c>Host-IP-Address</c> list in <c>Svc</c> provides the local
+IP address.
+If neither is specified then the default address selected by &gen_tcp;
+is used.
+In all cases, the selected address is either returned from
+&start; or passed in a <c>connected</c> message over the transport
+interface.</p>
</desc>
</func>
diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml
index 55b531155f..8bccf6521e 100644
--- a/lib/diameter/doc/src/diameter_transport.xml
+++ b/lib/diameter/doc/src/diameter_transport.xml
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd" [
<!ENTITY message '<seealso marker="#message">message()</seealso>'>
+ <!ENTITY MESSAGES '<seealso marker="#MESSAGES">MESSAGES</seealso>'>
+ <!ENTITY start '<seealso marker="#Mod:start-3">start/3</seealso>'>
<!ENTITY ip_address
'<seealso marker="kernel:inet#type-ip_address">inet:ip_address()</seealso>'>
<!ENTITY % also SYSTEM "seealso.ent" >
@@ -12,7 +14,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>
@@ -125,7 +127,7 @@ Ref is the value that was returned from the call to &mod_add_transport;
that has lead to starting of a transport process.</p>
<p>
-<c>Svc</c> contains the capabilities passed to &mod_start_service; and
+<c>Svc</c> contains capabilities passed to &mod_start_service; and
&mod_add_transport;, values passed to the latter overriding those
passed to the former.</p>
@@ -134,13 +136,16 @@ passed to the former.</p>
&mod_transport_opt; list passed to &mod_add_transport;.</p>
<p>
-The start function should use the <c>Host-IP-Address</c> list and/or
-<c>Config</c> to select an appropriate list of local IP addresses,
-and should return this list if different from the
-<c>#diameter_service{}</c> addresses.
-The returned list is used to populate <c>Host-IP-Address</c> AVPs in
-outgoing capabilities exchange messages, the
-<c>#diameter_service{}</c> addresses being used otherwise.</p>
+The start function should use the <c>Host-IP-Address</c> list in
+<c>Svc</c> and/or <c>Config</c> to select an appropriate list of local
+IP addresses, and should return this list if different from the
+<c>Svc</c> addresses.
+In the connecting case, the local address list can instead be
+communicated in a <c>connected</c> message (see &MESSAGES; below)
+following connection establishment.
+In either case, the local address list is used to populate
+<c>Host-IP-Address</c> AVPs in outgoing capabilities exchange
+messages.</p>
<p>
A transport process must implement the message interface documented below.
@@ -230,13 +235,16 @@ Not sent if the transport process has <c>Type=connect</c>.</p>
</item>
<tag><c>{diameter, {self(), connected, Remote}}</c></tag>
+<tag><c>{diameter, {self(), connected, Remote, [LocalAddr]}}</c></tag>
<item>
<p>
Inform the parent that the transport process with <c>Type=connect</c>
has established a connection with a peer.
-Not sent if the transport process has <c>Type=accept</c>.
+Not sent if the transport process has <c>Type=accept</c>.&nbsp;
<c>Remote</c> is an arbitrary term that uniquely identifies the remote
-endpoint to which the transport has connected.</p>
+endpoint to which the transport has connected.
+A <c>LocalAddr</c> list has the same semantics as one returned from
+&start;.</p>
</item>
<tag><c>{diameter, {recv, &message;}}</c></tag>
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 2daf84b0d4..ad61f12b5b 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -42,7 +42,7 @@ first.</p>
<!-- ===================================================================== -->
-<section><title>Diameter 1.4.1.1</title>
+<section><title>diameter 1.4.1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -79,7 +79,7 @@ first.</p>
</section>
-<section><title>Diameter 1.4.1</title>
+<section><title>diameter 1.4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -166,7 +166,7 @@ first.</p>
</section>
-<section><title>Diameter 1.4</title>
+<section><title>diameter 1.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -245,7 +245,7 @@ first.</p>
</section>
-<section><title>Diameter 1.3.1</title>
+<section><title>diameter 1.3.1</title>
<section><title>Known Bugs and Problems</title>
<list>
@@ -261,7 +261,7 @@ first.</p>
</section>
-<section><title>Diameter 1.3</title>
+<section><title>diameter 1.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -418,7 +418,7 @@ first.</p>
</section>
-<section><title>Diameter 1.2</title>
+<section><title>diameter 1.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -518,7 +518,7 @@ first.</p>
</section>
-<section><title>Diameter 1.1</title>
+<section><title>diameter 1.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -545,7 +545,7 @@ first.</p>
</section>
-<section><title>Diameter 1.0</title>
+<section><title>diameter 1.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -659,7 +659,7 @@ first.</p>
</section>
-<section><title>Diameter 0.10</title>
+<section><title>diameter 0.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl
index 8fdeba57bf..b4ee17e4b7 100644
--- a/lib/diameter/examples/code/peer.erl
+++ b/lib/diameter/examples/code/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
@@ -51,7 +51,6 @@
| {protocol(), ip_address(), non_neg_integer()}
| {protocol(), ip_address(), ip_address(), non_neg_integer()}.
--define(DEFAULT_ADDR, {127,0,0,1}).
-define(DEFAULT_PORT, 3868).
%% ---------------------------------------------------------------------------
@@ -111,7 +110,7 @@ server({T, Addr, Port}) ->
{port, Port}]}];
server(T) ->
- server({T, ?DEFAULT_ADDR, ?DEFAULT_PORT}).
+ server({T, loopback, ?DEFAULT_PORT}).
%% client/1
%%
@@ -124,21 +123,28 @@ client({all, LA, RA, RP}) ->
client({T, LA, RA, RP}) ->
[{transport_module, tmod(T)},
- {transport_config, [{ip, addr(LA)},
- {raddr, addr(RA)},
+ {transport_config, [{raddr, addr(RA)},
{rport, RP},
- {reuseaddr, true}]}];
+ {reuseaddr, true}
+ | ip(LA)]}];
-client({T, LA, RP}) ->
- client({T, LA, LA, RP});
+client({T, RA, RP}) ->
+ client({T, default, RA, RP});
client(T) ->
- client({T, ?DEFAULT_ADDR, ?DEFAULT_ADDR, ?DEFAULT_PORT}).
+ client({T, loopback, loopback, ?DEFAULT_PORT}).
tmod(tcp) -> diameter_tcp;
tmod(sctp) -> diameter_sctp.
-addr(default) ->
- ?DEFAULT_ADDR;
+ip(default) ->
+ [];
+ip(loopback) ->
+ [{ip, {127,0,0,1}}];
+ip(Addr) ->
+ [{ip, Addr}].
+
+addr(loopback) ->
+ {127,0,0,1};
addr(A) ->
A.
diff --git a/lib/diameter/examples/code/redirect_cb.erl b/lib/diameter/examples/code/redirect_cb.erl
index da31add70d..69836774a1 100644
--- a/lib/diameter/examples/code/redirect_cb.erl
+++ b/lib/diameter/examples/code/redirect_cb.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
@@ -20,7 +20,7 @@
-module(redirect_cb).
-include_lib("diameter/include/diameter.hrl").
--include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl").
%% diameter callbacks
-export([peer_up/3,
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index df10c33268..c0cf418f06 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -230,7 +230,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk
# Can't $(INSTALL_DIR) more than one directory at a time on Solaris.
release_spec: opt
- for d in bin ebin include src/dict; do \
+ for d in bin ebin examples include src/dict; do \
$(INSTALL_DIR) "$(RELSYSDIR)/$$d"; \
done
$(INSTALL_SCRIPT) $(BINS:%=../bin/%) "$(RELSYSDIR)/bin"
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 5f06cef020..57730cad61 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -45,6 +45,7 @@
-export_type([evaluable/0,
restriction/0,
+ remotes/0,
sequence/0,
app_alias/0,
service_name/0,
@@ -292,13 +293,20 @@ call(SvcName, App, Message) ->
| [node()]
| evaluable().
+-type remotes()
+ :: boolean()
+ | [node()]
+ | evaluable().
+
%% Options passed to start_service/2
-type service_opt()
:: capability()
| {application, [application_opt()]}
| {restrict_connections, restriction()}
- | {sequence, sequence() | evaluable()}.
+ | {sequence, sequence() | evaluable()}
+ | {share_peers, remotes()}
+ | {use_shared_peers, remotes()}.
-type application_opt()
:: {alias, app_alias()}
@@ -327,7 +335,7 @@ call(SvcName, App, Message) ->
-type transport_opt()
:: {transport_module, atom()}
| {transport_config, any()}
- | {transport_config, any(), non_neg_integer() | infinity}
+ | {transport_config, any(), 'Unsigned32'() | infinity}
| {applications, [app_alias()]}
| {capabilities, [capability()]}
| {capabilities_cb, evaluable()}
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 9f73815756..2a145c874b 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -103,6 +103,10 @@
%% Time to lay low before restarting a dead service.
-define(RESTART_SLEEP, 2000).
+%% Test for a valid timeout.
+-define(IS_UINT32(N),
+ is_integer(N) andalso 0 =< N andalso 0 == N bsr 32).
+
%% A minimal diameter_caps for checking for valid capabilities values.
-define(EXAMPLE_CAPS,
#diameter_caps{origin_host = "TheHost",
@@ -490,13 +494,11 @@ stop(SvcName) ->
%% has many.
add(SvcName, Type, Opts) ->
- %% Ensure usable capabilities. diameter_service:merge_service/2
- %% depends on this.
- lists:foreach(fun(Os) ->
- is_list(Os) orelse ?THROW({capabilities, Os}),
- ok = encode_CER(Os)
- end,
- [Os || {capabilities, Os} <- Opts, is_list(Os)]),
+ %% Ensure acceptable transport options. This won't catch all
+ %% possible errors (faulty callbacks for example) but it catches
+ %% many. diameter_service:merge_service/2 depends on usable
+ %% capabilities for example.
+ ok = transport_opts(Opts),
Ref = make_ref(),
T = {Ref, Type, Opts},
@@ -514,6 +516,61 @@ add(SvcName, Type, Opts) ->
No
end.
+transport_opts(Opts) ->
+ lists:foreach(fun(T) -> opt(T) orelse ?THROW({invalid, T}) end, Opts).
+
+opt({transport_module, M}) ->
+ is_atom(M);
+
+opt({transport_config, _, Tmo}) ->
+ ?IS_UINT32(Tmo) orelse Tmo == infinity;
+
+opt({applications, As}) ->
+ is_list(As);
+
+opt({capabilities, Os}) ->
+ is_list(Os) andalso ok == encode_CER(Os);
+
+opt({capx_timeout, Tmo}) ->
+ ?IS_UINT32(Tmo);
+
+opt({length_errors, T}) ->
+ lists:member(T, [exit, handle, discard]);
+
+opt({reconnect_timer, Tmo}) ->
+ ?IS_UINT32(Tmo);
+
+opt({watchdog_timer, {M,F,A}})
+ when is_atom(M), is_atom(F), is_list(A) ->
+ true;
+opt({watchdog_timer, Tmo}) ->
+ ?IS_UINT32(Tmo);
+
+opt({watchdog_config, L}) ->
+ is_list(L) andalso lists:all(fun wdopt/1, L);
+
+%% Options that we can't validate.
+opt({K, _})
+ when K == transport_config;
+ K == capabilities_cb;
+ K == disconnect_cb;
+ K == private ->
+ true;
+
+%% Anything else, which is ignored by us. This makes options sensitive
+%% to spelling mistakes but arbitrary options are passed by some users
+%% as a way to identify transports. (That is, can't just do away with
+%% it.)
+opt(_) ->
+ true.
+
+wdopt({K,N}) ->
+ (K == okay orelse K == suspect) andalso is_integer(N) andalso 0 =< N;
+wdopt(_) ->
+ false.
+
+%% start_transport/2
+
start_transport(SvcName, T) ->
case diameter_service:start_transport(SvcName, T) of
{ok, _Pid} ->
@@ -557,54 +614,69 @@ stop_transport(SvcName, Refs) ->
%% make_config/2
make_config(SvcName, Opts) ->
- Apps = init_apps(Opts),
+ AppOpts = [T || {application, _} = T <- Opts],
+ Apps = init_apps(AppOpts),
+
[] == Apps andalso ?THROW(no_apps),
%% Use the fact that diameter_caps has the same field names as CER.
Fields = ?BASE:'#info-'(diameter_base_CER) -- ['AVP'],
- COpts = [T || {K,_} = T <- Opts, lists:member(K, Fields)],
- Caps = make_caps(#diameter_caps{}, COpts),
+ CapOpts = [T || {K,_} = T <- Opts, lists:member(K, Fields)],
+ Caps = make_caps(#diameter_caps{}, CapOpts),
- ok = encode_CER(COpts),
+ ok = encode_CER(CapOpts),
- Os = split(Opts, fun opt/2, [{false, share_peers},
- {false, use_shared_peers},
- {false, monitor},
- {?NOMASK, sequence},
- {nodes, restrict_connections}]),
- %% share_peers and use_shared_peers are currently undocumented.
+ SvcOpts = make_opts((Opts -- AppOpts) -- CapOpts,
+ [{false, share_peers},
+ {false, use_shared_peers},
+ {false, monitor},
+ {?NOMASK, sequence},
+ {nodes, restrict_connections}]),
#service{name = SvcName,
rec = #diameter_service{applications = Apps,
capabilities = Caps},
- options = Os}.
+ options = SvcOpts}.
+
+make_opts(Opts, Defs) ->
+ Known = [{K, get_opt(K, Opts, D)} || {D,K} <- Defs],
+ Unknown = Opts -- Known,
-split(Opts, F, Defs) ->
- [{K, F(K, get_opt(K, Opts, D))} || {D,K} <- Defs].
+ [] == Unknown orelse ?THROW({invalid, hd(Unknown)}),
+
+ [{K, opt(K,V)} || {K,V} <- Known].
opt(K, false = B)
when K /= sequence ->
B;
opt(K, true = B)
- when K == share_peer;
+ when K == share_peers;
K == use_shared_peers ->
B;
-opt(monitor, P)
- when is_pid(P) ->
- P;
-
opt(restrict_connections, T)
when T == node;
- T == nodes;
- T == [];
- is_atom(hd(T)) ->
+ T == nodes ->
+ T;
+
+opt(K, T)
+ when (K == share_peers
+ orelse K == use_shared_peers
+ orelse K == restrict_connections), ([] == T
+ orelse is_atom(hd(T))) ->
T;
-opt(restrict_connections = K, F) ->
- try diameter_lib:eval(F) of %% no guarantee that it won't fail later
+opt(monitor, P)
+ when is_pid(P) ->
+ P;
+
+opt(K, F)
+ when K == restrict_connections;
+ K == share_peers;
+ K == use_shared_peers ->
+ try diameter_lib:eval(F) of %% but no guarantee that it won't fail later
Nodes when is_list(Nodes) ->
F;
V ->
@@ -629,7 +701,7 @@ opt(K, _) ->
?THROW({value, K}).
sequence({H,N} = T)
- when 0 =< N, N =< 32, 0 =< H, 0 == H bsr N ->
+ when 0 =< N, N =< 32, 0 =< H, 0 == H bsr (32-N) ->
T;
sequence(_) ->
@@ -664,8 +736,8 @@ encode_CER(Opts) ->
init_apps(Opts) ->
lists:foldl(fun app_acc/2, [], lists:reverse(Opts)).
-app_acc({application, Opts}, Acc) ->
- is_list(Opts) orelse ?THROW({application, Opts}),
+app_acc({application, Opts} = T, Acc) ->
+ is_list(Opts) orelse ?THROW(T),
[Dict, Mod] = get_opt([dictionary, module], Opts),
Alias = get_opt(alias, Opts, Dict),
@@ -681,9 +753,7 @@ app_acc({application, Opts}, Acc) ->
mutable = M,
options = [{answer_errors, A},
{request_errors, P}]}
- | Acc];
-app_acc(_, Acc) ->
- Acc.
+ | Acc].
init_mod(#diameter_callback{} = R) ->
init_mod([diameter_callback, R]);
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
index 362d593b24..44d81e2778 100644
--- a/lib/diameter/src/base/diameter_lib.erl
+++ b/lib/diameter/src/base/diameter_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,77 +19,86 @@
-module(diameter_lib).
--export([report/2, info_report/2,
+-export([info_report/2,
error_report/2,
warning_report/2,
now_diff/1,
time/1,
eval/1,
- ip4address/1,
- ip6address/1,
ipaddr/1,
spawn_opts/2,
wait/1,
fold_tuple/3,
log/4]).
--include("diameter_internal.hrl").
-
%% ---------------------------------------------------------------------------
-%% # info_report(Reason, MFA)
-%%
-%% Input: Reason = Arbitrary term indicating the reason for the report.
-%% MFA = {Module, Function, Args} to report.
-%%
-%% Output: true
+%% # info_report/2
%% ---------------------------------------------------------------------------
-report(Reason, MFA) ->
- info_report(Reason, MFA).
+-spec info_report(Reason :: term(), T :: term())
+ -> true.
-info_report(Reason, MFA) ->
- report(fun error_logger:info_report/1, Reason, MFA),
+info_report(Reason, T) ->
+ report(fun error_logger:info_report/1, Reason, T),
true.
-%%% ---------------------------------------------------------------------------
-%%% # error_report(Reason, MFA)
-%%% # warning_report(Reason, MFA)
-%%%
-%%% Output: false
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # error_report/2
+%% # warning_report/2
+%% ---------------------------------------------------------------------------
+
+-spec error_report(Reason :: term(), T :: term())
+ -> false.
+
+error_report(Reason, T) ->
+ report(fun error_logger:error_report/1, Reason, T).
-error_report(Reason, MFA) ->
- report(fun error_logger:error_report/1, Reason, MFA).
+-spec warning_report(Reason :: term(), T :: term())
+ -> false.
-warning_report(Reason, MFA) ->
- report(fun error_logger:warning_report/1, Reason, MFA).
+warning_report(Reason, T) ->
+ report(fun error_logger:warning_report/1, Reason, T).
-report(Fun, Reason, MFA) ->
- Fun([{why, Reason}, {who, self()}, {what, MFA}]),
+report(Fun, Reason, T) ->
+ Fun([{why, Reason}, {who, self()}, {what, T}]),
false.
-%%% ---------------------------------------------------------------------------
-%%% # now_diff(Time)
-%%%
-%%% Description: Return timer:now_diff(now(), Time) as an {H, M, S, MicroS}
-%%% tuple instead of as integer microseconds.
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # now_diff/1
+%% ---------------------------------------------------------------------------
+
+-spec now_diff(NowT)
+ -> {Hours, Mins, Secs, MicroSecs}
+ when NowT :: {non_neg_integer(), 0..999999, 0..999999},
+ Hours :: non_neg_integer(),
+ Mins :: 0..59,
+ Secs :: 0..59,
+ MicroSecs :: 0..999999.
+
+%% Return timer:now_diff(now(), NowT) as an {H, M, S, MicroS} tuple
+%% instead of as integer microseconds.
now_diff({_,_,_} = Time) ->
- time(timer:now_diff(erlang:now(), Time)).
-
-%%% ---------------------------------------------------------------------------
-%%% # time(Time)
-%%%
-%%% Input: Time = {MegaSec, Sec, MicroSec}
-%%% | MicroSec
-%%%
-%%% Output: {H, M, S, MicroS}
-%%% ---------------------------------------------------------------------------
-
-time({_,_,_} = Time) -> %% time of day
+ time(timer:now_diff(now(), Time)).
+
+%% ---------------------------------------------------------------------------
+%% # time/1
+%%
+%% Return an elapsed time as an {H, M, S, MicroS} tuple.
+%% ---------------------------------------------------------------------------
+
+-spec time(NowT | Diff)
+ -> {Hours, Mins, Secs, MicroSecs}
+ when NowT :: {non_neg_integer(), 0..999999, 0..999999},
+ Diff :: non_neg_integer(),
+ Hours :: non_neg_integer(),
+ Mins :: 0..59,
+ Secs :: 0..59,
+ MicroSecs :: 0..999999.
+
+time({_,_,_} = NowT) -> %% time of day
%% 24 hours = 24*60*60*1000000 = 86400000000 microsec
- time(timer:now_diff(Time, {0,0,0}) rem 86400000000);
+ time(timer:now_diff(NowT, {0,0,0}) rem 86400000000);
time(Micro) -> %% elapsed time
Seconds = Micro div 1000000,
@@ -98,9 +107,21 @@ time(Micro) -> %% elapsed time
S = Seconds rem 60,
{H, M, S, Micro rem 1000000}.
-%%% ---------------------------------------------------------------------------
-%%% # eval(Func)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # eval/1
+%%
+%% Evaluate a function in various forms.
+%% ---------------------------------------------------------------------------
+
+-type f() :: {module(), atom(), list()}
+ | nonempty_maybe_improper_list(fun(), list())
+ | fun().
+
+-spec eval(Fun)
+ -> term()
+ when Fun :: f()
+ | {f()}
+ | nonempty_maybe_improper_list(f(), list()).
eval({M,F,A}) ->
apply(M,F,A);
@@ -120,66 +141,15 @@ eval({F}) ->
eval(F) ->
F().
-%%% ---------------------------------------------------------------------------
-%%% # ip4address(Addr)
-%%%
-%%% Input: string() (eg. "10.0.0.1")
-%%% | list of integer()
-%%% | tuple of integer()
-%%%
-%%% Output: {_,_,_,_} of integer
-%%%
-%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
-%%% ---------------------------------------------------------------------------
-
-ip4address([_,_,_,_] = Addr) -> %% Length 4 string can't be an address.
- ipaddr(list_to_tuple(Addr));
-
-%% Be brutal.
-ip4address(Addr) ->
- try
- {_,_,_,_} = ipaddr(Addr)
- catch
- error: _ ->
- erlang:error({invalid_address, Addr, ?STACK})
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # ip6address(Addr)
-%%%
-%%% Input: string() (eg. "1080::8:800:200C:417A")
-%%% | list of integer()
-%%% | tuple of integer()
-%%%
-%%% Output: {_,_,_,_,_,_,_,_} of integer
-%%%
-%%% Exceptions: error: {invalid_address, Addr, erlang:get_stacktrace()}
-%%% ---------------------------------------------------------------------------
-
-ip6address([_,_,_,_,_,_,_,_] = Addr) -> %% Length 8 string can't be an address.
- ipaddr(list_to_tuple(Addr));
-
-%% Be brutal.
-ip6address(Addr) ->
- try
- {_,_,_,_,_,_,_,_} = ipaddr(Addr)
- catch
- error: _ ->
- erlang:error({invalid_address, Addr, ?STACK})
- end.
-
-%%% ---------------------------------------------------------------------------
-%%% # ipaddr(Addr)
-%%%
-%%% Input: string() | tuple of integer()
-%%%
-%%% Output: {_,_,_,_} | {_,_,_,_,_,_,_,_}
-%%%
-%%% Exceptions: error: {invalid_address, erlang:get_stacktrace()}
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # ipaddr/1
+%%
+%% Parse an IP address.
+%% ---------------------------------------------------------------------------
--spec ipaddr(string() | tuple())
- -> inet:ip_address().
+-spec ipaddr([byte()] | tuple())
+ -> inet:ip_address()
+ | none().
%% Don't convert lists of integers since a length 8 list like
%% [$1,$0,$.,$0,$.,$0,$.,$1] is ambiguous: is it "10.0.0.1" or
@@ -193,7 +163,7 @@ ipaddr(Addr) ->
ip(Addr)
catch
error: _ ->
- erlang:error({invalid_address, ?STACK})
+ erlang:error({invalid_address, erlang:get_stacktrace()})
end.
%% Already a tuple: ensure non-negative integers of the right size.
@@ -210,11 +180,12 @@ ip(Addr) ->
{ok, A} = inet_parse:address(Addr), %% documented in inet(3)
A.
-%%% ---------------------------------------------------------------------------
-%%% # spawn_opts(Type, Opts)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # spawn_opts/2
+%% ---------------------------------------------------------------------------
-%% TODO: config variables.
+-spec spawn_opts(server|worker, list())
+ -> list().
spawn_opts(server, Opts) ->
opts(75000, Opts);
@@ -224,24 +195,32 @@ spawn_opts(worker, Opts) ->
opts(HeapSize, Opts) ->
[{min_heap_size, HeapSize} | lists:keydelete(min_heap_size, 1, Opts)].
-%%% ---------------------------------------------------------------------------
-%%% # wait(MRefs)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # wait/1
+%% ---------------------------------------------------------------------------
+
+-spec wait([pid()])
+ -> ok.
wait(L) ->
- w([erlang:monitor(process, P) || P <- L]).
+ down([erlang:monitor(process, P) || P <- L]).
-w([]) ->
+down([]) ->
ok;
-w(L) ->
- receive
- {'DOWN', MRef, process, _, _} ->
- w(lists:delete(MRef, L))
- end.
+down([MRef|T]) ->
+ receive {'DOWN', MRef, process, _, _} -> ok end,
+ down(T).
-%%% ---------------------------------------------------------------------------
-%%% # fold_tuple(N, T0, T)
-%%% ---------------------------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # fold_tuple/3
+%% ---------------------------------------------------------------------------
+
+-spec fold_tuple(N, T0, T)
+ -> tuple()
+ when N :: pos_integer(),
+ T0 :: tuple(),
+ T :: tuple()
+ | undefined.
%% Replace fields in T0 by those of T starting at index N, unless the
%% new value is 'undefined'.
@@ -262,11 +241,11 @@ ft(undefined, {_, T}) ->
ft(Value, {Idx, T}) ->
setelement(Idx, T, Value).
-%%% ----------------------------------------------------------
-%%% # log(Slogan, Mod, Line, Details)
-%%%
-%%% Called to have something to trace on for happenings of interest.
-%%% ----------------------------------------------------------
+%% ---------------------------------------------------------------------------
+%% # log/4
+%%
+%% Called to have something to trace on for happenings of interest.
+%% ---------------------------------------------------------------------------
-log(_, _, _, _) ->
+log(_Slogan, _Mod, _Line, _Details) ->
ok.
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index 130bedda84..0d2efd4d1f 100644
--- a/lib/diameter/src/base/diameter_peer.erl
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -24,14 +24,15 @@
%% Interface towards transport modules ...
-export([recv/2,
up/1,
- up/2]).
+ up/2,
+ up/3]).
%% ... and the stack.
-export([start/1,
send/2,
close/1,
abort/1,
- notify/2]).
+ notify/3]).
%% Server start.
-export([start_link/0]).
@@ -63,11 +64,11 @@
-define(DEFAULT_TTMO, infinity).
%%% ---------------------------------------------------------------------------
-%%% # notify/2
+%%% # notify/3
%%% ---------------------------------------------------------------------------
-notify(SvcName, T) ->
- rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}).
+notify(Nodes, SvcName, T) ->
+ rpc:abcast(Nodes, ?SERVER, {notify, SvcName, T}).
%%% ---------------------------------------------------------------------------
%%% # start/1
@@ -180,7 +181,7 @@ start(Mod, Args) ->
apply(Mod, start, Args).
%%% ---------------------------------------------------------------------------
-%%% # up/[12]
+%%% # up/1-3
%%% ---------------------------------------------------------------------------
up(Pid) -> %% accepting transport
@@ -189,6 +190,9 @@ up(Pid) -> %% accepting transport
up(Pid, Remote) -> %% connecting transport
ifc_send(Pid, {self(), connected, Remote}).
+up(Pid, Remote, LAddrs) -> %% connecting transport
+ ifc_send(Pid, {self(), connected, Remote, LAddrs}).
+
%%% ---------------------------------------------------------------------------
%%% # recv/2
%%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 66342f7b62..6be4259510 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -198,6 +198,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {Mask,
OnLengthErr = proplists:get_value(length_errors, Opts, exit),
lists:member(OnLengthErr, [exit, handle, discard])
orelse ?ERROR({invalid, {length_errors, OnLengthErr}}),
+ %% Error checking is for configuration added in old code.
{TPid, Addrs} = start_transport(T, Rest, Svc),
@@ -212,9 +213,6 @@ i({Ack, WPid, {M, Ref} = T, Opts, {Mask,
%% transports on the same service can use different local addresses.
%% The local addresses are put into Host-IP-Address avps here when
%% sending capabilities exchange messages.
-%%
-%% Invalid transport config may cause us to crash but note that the
-%% watchdog start (start/2) succeeds regardless.
%% Wait for the caller to have a monitor to avoid a race with our
%% death. (Since the exit reason is used in diameter_service.)
@@ -353,10 +351,17 @@ transition({diameter, {TPid, connected, Remote}},
mode = M}
= S) ->
{'Wait-Conn-Ack', _} = PS, %% assert
- connect = M, %%
+ connect = M, %%
keep_transport(TPid),
send_CER(S#state{mode = {M, Remote}});
+transition({diameter, {TPid, connected, Remote, LAddrs}},
+ #state{transport = TPid,
+ service = Svc}
+ = S) ->
+ transition({diameter, {TPid, connected, Remote}},
+ S#state{service = readdr(Svc, LAddrs)});
+
%% Connection from peer.
transition({diameter, {TPid, connected}},
#state{transport = TPid,
@@ -365,7 +370,7 @@ transition({diameter, {TPid, connected}},
parent = Pid}
= S) ->
{'Wait-Conn-Ack', Tmo} = PS, %% assert
- accept = M, %%
+ accept = M, %%
keep_transport(TPid),
Pid ! {accepted, self()},
start_timer(Tmo, S#state{state = recv_CER});
@@ -378,6 +383,8 @@ transition({diameter, {_, connected}}, _) ->
{stop, connection_timeout};
transition({diameter, {_, connected, _}}, _) ->
{stop, connection_timeout};
+transition({diameter, {_, connected, _, _}}, _) ->
+ {stop, connection_timeout};
%% Connection has timed out: start an alternate.
transition({connection_timeout = T, TPid},
@@ -846,8 +853,12 @@ a('DPR', #diameter_caps{origin_host = {Host, _},
%% recv_CER/2
recv_CER(CER, #state{service = Svc, dictionary = Dict}) ->
- {ok, T} = diameter_capx:recv_CER(CER, Svc, Dict),
- T.
+ case diameter_capx:recv_CER(CER, Svc, Dict) of
+ {ok, T} ->
+ T;
+ {error, Reason} ->
+ close({'CER', CER, Svc, Dict, Reason})
+ end.
%% handle_CEA/1
@@ -907,8 +918,12 @@ recv_CEA(#diameter_packet{header = #diameter_header{version
errors = []},
#state{service = Svc,
dictionary = Dict}) ->
- {ok, T} = diameter_capx:recv_CEA(CEA, Svc, Dict),
- T;
+ case diameter_capx:recv_CEA(CEA, Svc, Dict) of
+ {ok, T} ->
+ T;
+ {error, Reason} ->
+ close({'CEA', CEA, Svc, Dict, Reason})
+ end;
recv_CEA(Pkt, S) ->
close({'CEA', caps(S), Pkt}).
@@ -987,8 +1002,17 @@ capz(#diameter_caps{} = L, #diameter_caps{} = R) ->
%% close/1
close(Reason) ->
+ report(Reason),
throw({?MODULE, close, Reason}).
+%% Could possibly log more here.
+report({M, _, _, _, _} = T)
+ when M == 'CER';
+ M == 'CEA' ->
+ diameter_lib:error_report(failure, T);
+report(_) ->
+ ok.
+
%% dwa/1
dwa(#diameter_caps{origin_host = OH,
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index c971646861..112e83476d 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -125,9 +125,9 @@
monitor = false :: false | pid(), %% process to die with
options
:: [{sequence, diameter:sequence()} %% sequence mask
- | {restrict_connections, diameter:restriction()}
- | {share_peers, boolean()} %% broadcast peers to remote nodes?
- | {use_shared_peers, boolean()}]}).%% use broadcasted peers?
+ | {share_peers, diameter:remotes()} %% broadcast to
+ | {use_shared_peers, diameter:remotes()} %% use from
+ | {restrict_connections, diameter:restriction()}]}).
%% shared_peers reflects the peers broadcast from remote nodes.
%% Record representing an RFC 3539 watchdog process implemented by
@@ -681,11 +681,9 @@ mref(false = No) ->
mref(P) ->
erlang:monitor(process, P).
-init_shared(#state{options = [_, _, {_, true} | _],
+init_shared(#state{options = [_, _, {_,T} | _],
service_name = Svc}) ->
- diameter_peer:notify(Svc, {service, self()});
-init_shared(#state{options = [_, _, {_, false} | _]}) ->
- ok.
+ notify(T, Svc, {service, self()}).
init_mod(#diameter_app{alias = Alias,
init_state = S}) ->
@@ -698,6 +696,37 @@ get_value(Key, Vs) ->
{_, V} = lists:keyfind(Key, 1, Vs),
V.
+notify(Share, SvcName, T) ->
+ Nodes = remotes(Share),
+ [] /= Nodes andalso diameter_peer:notify(Nodes, SvcName, T).
+%% Test for the empty list for upgrade reasons: there's no
+%% diameter_peer:notify/3 in old code so no call means no load order
+%% requirement.
+
+remotes(false) ->
+ [];
+
+remotes(true) ->
+ nodes();
+
+remotes(Nodes)
+ when is_atom(hd(Nodes));
+ Nodes == [] ->
+ Nodes;
+
+remotes(F) ->
+ try diameter_lib:eval(F) of
+ L when is_list(L) ->
+ L;
+ T ->
+ diameter_lib:error_report({invalid_return, T}, F),
+ []
+ catch
+ E:R ->
+ diameter_lib:error_report({failure, {E, R, ?STACK}}, F),
+ []
+ end.
+
%% ---------------------------------------------------------------------------
%% # start/3
%% ---------------------------------------------------------------------------
@@ -832,17 +861,21 @@ watchdog(TPid, [], ?WD_SUSPECT, ?WD_OKAY, Wd, State) ->
%% Watchdog has an unresponsive connection.
watchdog(TPid, [], ?WD_OKAY, ?WD_SUSPECT = To, Wd, State) ->
#watchdog{peer = TPid} = Wd, %% assert
- connection_down(Wd, To, State);
+ watchdog_down(Wd, To, State);
%% Watchdog has lost its connection.
watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{peerT = PeerT} = S) ->
close(Wd, S),
- connection_down(Wd, To, S),
+ watchdog_down(Wd, To, S),
ets:delete(PeerT, TPid);
watchdog(_, [], _, _, _, _) ->
ok.
+watchdog_down(Wd, To, #state{watchdogT = WatchdogT} = S) ->
+ insert(WatchdogT, Wd#watchdog{state = To}),
+ connection_down(Wd, To, S).
+
%% ---------------------------------------------------------------------------
%% # connection_up/3
%% ---------------------------------------------------------------------------
@@ -1000,21 +1033,17 @@ connection_down(#watchdog{state = ?WD_OKAY,
remove_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
diameter_traffic:peer_down(TPid);
-connection_down(#watchdog{}, #peer{}, _) ->
- ok;
-
-connection_down(#watchdog{state = WS,
+connection_down(#watchdog{state = ?WD_OKAY,
peer = TPid}
= Wd,
To,
- #state{watchdogT = WatchdogT,
- peerT = PeerT}
+ #state{peerT = PeerT}
= S)
when is_atom(To) ->
- insert(WatchdogT, Wd#watchdog{state = To}),
- ?WD_OKAY == WS
- andalso
- connection_down(Wd, fetch(PeerT, TPid), S).
+ connection_down(Wd, #peer{} = fetch(PeerT, TPid), S);
+
+connection_down(#watchdog{}, _, _) ->
+ ok.
remove_local_peer(SApps, T, LDict) ->
lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
@@ -1233,12 +1262,12 @@ report_status(Status,
peer = TPid,
type = Type,
options = Opts},
- #peer{apps = [_|_] = As,
+ #peer{apps = [_|_] = Apps,
caps = Caps},
#state{service_name = SvcName}
= S,
Extra) ->
- share_peer(Status, Caps, As, TPid, S),
+ share_peer(Status, Caps, Apps, TPid, S),
Info = [Status, Ref, {TPid, Caps}, {type(Type), Opts} | Extra],
send_event(SvcName, list_to_tuple(Info)).
@@ -1255,9 +1284,9 @@ send_event(#diameter_event{service = SvcName} = E) ->
%% # share_peer/5
%% ---------------------------------------------------------------------------
-share_peer(up, Caps, Aliases, TPid, #state{options = [_, {_, true} | _],
- service_name = Svc}) ->
- diameter_peer:notify(Svc, {peer, TPid, Aliases, Caps});
+share_peer(up, Caps, Apps, TPid, #state{options = [_, {_,T} | _],
+ service_name = Svc}) ->
+ notify(T, Svc, {peer, TPid, [A || {_,A} <- Apps], Caps});
share_peer(_, _, _, _, _) ->
ok.
@@ -1266,34 +1295,34 @@ share_peer(_, _, _, _, _) ->
%% # share_peers/2
%% ---------------------------------------------------------------------------
-share_peers(Pid, #state{options = [_, {_, true} | _],
- local_peers = PDict}) ->
- ?Dict:fold(fun(A,Ps,ok) -> sp(Pid, A, Ps), ok end, ok, PDict);
-
-share_peers(_, _) ->
- ok.
+share_peers(Pid, #state{options = [_, {_,T} | _], local_peers = PDict}) ->
+ is_remote(Pid, T)
+ andalso ?Dict:fold(fun(A,Ps,ok) -> sp(Pid, A, Ps), ok end, ok, PDict).
sp(Pid, Alias, Peers) ->
lists:foreach(fun({P,C}) -> Pid ! {peer, P, [Alias], C} end, Peers).
+is_remote(Pid, T) ->
+ Node = node(Pid),
+ Node /= node() andalso lists:member(Node, remotes(T)).
+
%% ---------------------------------------------------------------------------
%% # remote_peer_up/4
%% ---------------------------------------------------------------------------
-remote_peer_up(Pid, Aliases, Caps, #state{options = [_, _, {_, true} | _],
- service = Svc,
- shared_peers = PDict}) ->
+remote_peer_up(Pid, Aliases, Caps, #state{options = [_, _, {_,T} | _]} = S) ->
+ is_remote(Pid, T)
+ andalso rpu(Pid, Aliases, Caps, S).
+
+rpu(Pid, Aliases, Caps, #state{service = Svc, shared_peers = PDict}) ->
#diameter_service{applications = Apps} = Svc,
Key = #diameter_app.alias,
- As = lists:filter(fun(A) -> lists:keymember(A, Key, Apps) end, Aliases),
- rpu(Pid, Caps, PDict, As);
+ F = fun(A) -> lists:keymember(A, Key, Apps) end,
+ rpu(Pid, lists:filter(F, Aliases), Caps, PDict);
-remote_peer_up(_, _, _, #state{options = [_, _, {_, false} | _]}) ->
- ok.
-
-rpu(_, _, PDict, []) ->
- PDict;
-rpu(Pid, Caps, PDict, Aliases) ->
+rpu(_, [] = No, _, _) ->
+ No;
+rpu(Pid, Aliases, Caps, PDict) ->
erlang:monitor(process, Pid),
T = {Pid, Caps},
lists:foreach(fun(A) -> ?Dict:append(A, T, PDict) end, Aliases).
@@ -1302,8 +1331,7 @@ rpu(Pid, Caps, PDict, Aliases) ->
%% # remote_peer_down/2
%% ---------------------------------------------------------------------------
-remote_peer_down(Pid, #state{options = [_, _, {_, true} | _],
- shared_peers = PDict}) ->
+remote_peer_down(Pid, #state{shared_peers = PDict}) ->
lists:foreach(fun(A) -> rpd(Pid, A, PDict) end, ?Dict:fetch_keys(PDict)).
rpd(Pid, Alias, PDict) ->
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index f527f7c754..25b902e3f2 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -1479,12 +1479,14 @@ send({TPid, Pkt, #request{handler = Pid} = Req, SvcName, Timeout, TRef}) ->
Req#request{handler = self()},
SvcName,
Timeout),
- Pid ! reref(receive T -> T end, Ref, TRef).
-
-reref({T, Ref, R}, Ref, TRef) ->
- {T, TRef, R};
-reref(T, _, _) ->
- T.
+ receive
+ {answer, _, _, _, _} = A ->
+ Pid ! A;
+ {failover = T, Ref} ->
+ Pid ! {T, TRef};
+ T ->
+ exit({timeout, Ref, TPid} = T)
+ end.
%% send/2
@@ -1559,7 +1561,7 @@ resend_request(Pkt0,
store_request(TPid, Bin, Req, Timeout) ->
Seqs = diameter_codec:sequence_numbers(Bin),
- TRef = erlang:start_timer(Timeout, self(), timeout),
+ TRef = erlang:start_timer(Timeout, self(), TPid),
ets:insert(?REQUEST_TABLE, {Seqs, Req, TRef}),
ets:member(?REQUEST_TABLE, TPid)
orelse (self() ! {failover, TRef}), %% failover/1 may have missed
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index 82ca603cf3..41c493ff20 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -158,7 +158,7 @@ wait(Ref, Pid) ->
config(Opts) ->
Config = proplists:get_value(watchdog_config, Opts, []),
is_list(Config) orelse config_error({watchdog_config, Config}),
- lists:foldl(fun config/2, #config{}, Config).
+ lists:foldl(fun config/2, #config{}, Config). %% ^ added in old code
config({suspect, N}, Rec)
when ?IS_NATURAL(N) ->
@@ -168,7 +168,7 @@ config({okay, N}, Rec)
when ?IS_NATURAL(N) ->
Rec#config{okay = N};
-config(T, _) ->
+config(T, _) -> %% added in old code
config_error(T).
%% start/5
@@ -225,7 +225,8 @@ dict0(_, _, Acc) ->
Acc.
config_error(T) ->
- ?ERROR({configuration_error, T}).
+ diameter_lib:error_report(configuration_error, T),
+ exit({shutdown, {configuration_error, T}}).
%% handle_call/3
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 132088b514..cbbba714ac 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -100,6 +100,18 @@
%% # start/3
%% ---------------------------------------------------------------------------
+-spec start({accept, Ref}, Svc, [Opt])
+ -> {ok, pid(), [inet:ip_address()]}
+ when Ref :: diameter:transport_ref(),
+ Svc :: #diameter_service{},
+ Opt :: diameter:transport_opt();
+ ({connect, Ref}, Svc, [Opt])
+ -> {ok, pid(), [inet:ip_address()]}
+ | {ok, pid()}
+ when Ref :: diameter:transport_ref(),
+ Svc :: #diameter_service{},
+ Opt :: diameter:transport_opt().
+
start({T, Ref}, #diameter_service{capabilities = Caps}, Opts) ->
diameter_tcp_sup:start(), %% start tcp supervisors on demand
{Mod, Rest} = split(Opts),
@@ -172,7 +184,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs})
OwnOpts,
?DEFAULT_FRAGMENT_TIMEOUT),
?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}),
- Sock = i(T, Ref, Mod, Pid, SslOpts, Rest, Addrs),
+ Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs),
MPid ! {stop, self()}, %% tell the monitor to die
M = if SslOpts -> ssl; true -> Mod end,
setopts(M, Sock),
@@ -199,14 +211,21 @@ i(#monitor{parent = Pid, transport = TPid} = S) ->
i({listen, LRef, APid, {Mod, Opts, Addrs}}) ->
{[LA, LP], Rest} = proplists:split(Opts, [ip, port]),
- LAddr = get_addr(LA, Addrs),
+ LAddrOpt = get_addr(LA, Addrs),
LPort = get_port(LP),
- {ok, LSock} = Mod:listen(LPort, gen_opts(LAddr, Rest)),
+ {ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)),
+ LAddr = laddr(LAddrOpt, Mod, LSock),
true = diameter_reg:add_new({?MODULE, listener, {LRef, {LAddr, LSock}}}),
proc_lib:init_ack({ok, self(), {LAddr, LSock}}),
erlang:monitor(process, APid),
start_timer(#listener{socket = LSock}).
+laddr([], Mod, Sock) ->
+ {ok, {Addr, _Port}} = sockname(Mod, Sock),
+ Addr;
+laddr([{ip, Addr}], _, _) ->
+ Addr.
+
own(Opts) ->
{Own, Rest} = proplists:split(Opts, [fragment_timer]),
{lists:append(Own), Rest}.
@@ -225,17 +244,19 @@ ssl_opts([{ssl_options, Opts}])
ssl_opts(L) ->
?ERROR({ssl_options, L}).
-%% i/7
+%% init/7
%% Establish a TLS connection before capabilities exchange ...
-i(Type, Ref, Mod, Pid, true, Opts, Addrs) ->
- i(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs);
+init(Type, Ref, Mod, Pid, true, Opts, Addrs) ->
+ init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs);
%% ... or not.
-i(Type, Ref, Mod, Pid, _, Opts, Addrs) ->
- i(Type, Ref, Mod, Pid, Opts, Addrs).
+init(Type, Ref, Mod, Pid, _, Opts, Addrs) ->
+ init(Type, Ref, Mod, Pid, Opts, Addrs).
+
+%% init/6
-i(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
{LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
@@ -243,17 +264,28 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) ->
diameter_peer:up(Pid),
Sock;
-i(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
+init(connect = T, Ref, Mod, Pid, Opts, Addrs) ->
{[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]),
- LAddr = get_addr(LA, Addrs),
- RAddr = get_addr(RA, []),
+ LAddrOpt = get_addr(LA, Addrs),
+ RAddr = get_addr(RA),
RPort = get_port(RP),
- proc_lib:init_ack({ok, self(), [LAddr]}),
- Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))),
+ proc_lib:init_ack(init_rc(LAddrOpt)),
+ Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddrOpt, Rest))),
publish(Mod, T, Ref, Sock),
- diameter_peer:up(Pid, {RAddr, RPort}),
+ up(Pid, {RAddr, RPort}, LAddrOpt, Mod, Sock),
Sock.
+init_rc([{ip, Addr}]) ->
+ {ok, self(), [Addr]};
+init_rc([]) ->
+ {ok, self()}.
+
+up(Pid, Remote, [{ip, _Addr}], _, _) ->
+ diameter_peer:up(Pid, Remote);
+up(Pid, Remote, [], Mod, Sock) ->
+ {Addr, _Port} = ok(sockname(Mod, Sock)),
+ diameter_peer:up(Pid, Remote, [Addr]).
+
publish(Mod, T, Ref, Sock) ->
true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}),
putr(?INFO_KEY, {Mod, Sock}). %% for info/1
@@ -281,10 +313,17 @@ l([], LRef, T) ->
{ok, _, AS} = diameter_tcp_sup:start_child({listen, LRef, self(), T}),
AS.
+%% get_addr/1
+
+get_addr(As) ->
+ diameter_lib:ipaddr(addr(As, [])).
+
%% get_addr/2
+get_addr([], []) ->
+ [];
get_addr(As, Def) ->
- diameter_lib:ipaddr(addr(As, Def)).
+ [{ip, diameter_lib:ipaddr(addr(As, Def))}].
%% Take the first address from the service if several are unspecified.
addr([], [Addr | _]) ->
@@ -305,14 +344,10 @@ get_port(Ps) ->
%% gen_opts/2
-gen_opts(LAddr, Opts) ->
+gen_opts(LAddrOpt, Opts) ->
{L,_} = proplists:split(Opts, [binary, packet, active]),
[[],[],[]] == L orelse ?ERROR({reserved_options, Opts}),
- [binary,
- {packet, 0},
- {active, once},
- {ip, LAddr}
- | Opts].
+ [binary, {packet, 0}, {active, once}] ++ LAddrOpt ++ Opts.
%% ---------------------------------------------------------------------------
%% # ports/1
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index 061f0bcbef..9719c67b32 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -93,6 +93,11 @@ info:
@$(call list,HRL_FILES)
@echo
@$(call list,SUITES)
+ @echo
+ @echo erl = $(shell which erl)
+ @erl -noinput \
+ -eval 'io:format("diameter = ~s~n", [code:lib_dir(diameter)])' \
+ -s init stop
@echo ========================================
help:
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index 9e6619ecdd..8c9bb67e61 100644
--- a/lib/diameter/test/diameter_capx_SUITE.erl
+++ b/lib/diameter/test/diameter_capx_SUITE.erl
@@ -444,7 +444,7 @@ connect(Config, T, Opts) ->
{CRef, LRef}.
connect(LRef, Opts) ->
- [PortNr] = ?util:lport(tcp, LRef, 20),
+ [PortNr] = ?util:lport(tcp, LRef),
{ok, CRef} = diameter:add_transport(?CLIENT,
{connect, opts(PortNr, Opts)}),
CRef.
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index dc8cbffc83..0baac59c1a 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -65,7 +65,7 @@ lib(N, {_,_} = T) ->
lib(IP, B) ->
LA = tuple_to_list(IP),
{SA,Fun} = ip(LA),
- [] = run([[fun lib/4, IP, B, Fun, A] || A <- [IP, LA, SA]]).
+ [] = run([[fun lib/4, IP, B, Fun, A] || A <- [IP, SA]]).
lib(IP, B, Fun, A) ->
try Fun(A) of
@@ -78,10 +78,10 @@ lib(IP, B, Fun, A) ->
ip([_,_,_,_] = A) ->
[$.|S] = lists:append(["." ++ integer_to_list(N) || N <- A]),
- {S, fun diameter_lib:ip4address/1};
+ {S, fun diameter_lib:ipaddr/1};
ip([_,_,_,_,_,_,_,_] = A) ->
[$:|S] = lists:flatten([":" ++ io_lib:format("~.16B", [N]) || N <- A]),
- {S, fun diameter_lib:ip6address/1}.
+ {S, fun diameter_lib:ipaddr/1}.
%% ------------------------------------------------------------------------
%% base/1
diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl
index 79bf9d32db..81722c8dca 100644
--- a/lib/diameter/test/diameter_compiler_SUITE.erl
+++ b/lib/diameter/test/diameter_compiler_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
@@ -31,8 +31,7 @@
%% testcases
-export([format/1, format/2,
replace/1, replace/2,
- generate/1, generate/4,
- examples/1]).
+ generate/1, generate/4]).
-export([dict/0]). %% fake dictionary module
@@ -328,14 +327,6 @@
"@codecs mymod "
"Origin-Host Origin-Realm\n&"}]}]).
-%% Standard dictionaries in examples/dict.
--define(EXAMPLES, [rfc4004_mip,
- rfc4005_nas,
- rfc4006_cc,
- rfc4072_eap,
- rfc4590_digest,
- rfc4740_sip]).
-
%% ===========================================================================
suite() ->
@@ -344,8 +335,7 @@ suite() ->
all() ->
[format,
replace,
- generate,
- examples].
+ generate].
%% Error handling testcases will make an erroneous dictionary out of
%% the base dictionary and check that the expected error results.
@@ -429,41 +419,6 @@ generate(Mods, Bin, N, Mode) ->
andalso ({ok, _} = compile:file(File ++ ".erl", [return_errors])).
%% ===========================================================================
-%% examples/1
-%%
-%% Compile dictionaries extracted from various standards.
-
-examples(_Config) ->
- Dir = filename:join([code:lib_dir(diameter, examples), "dict"]),
- [D || D <- ?EXAMPLES, _ <- [examples(?S(D), Dir)]].
-
-examples(Dict, Dir) ->
- {Name, Pre} = make_name(Dict),
- ok = diameter_make:codec(filename:join([Dir, Dict ++ ".dia"]),
- [{name, Name},
- {prefix, Pre},
- inherits("rfc3588_base")
- | opts(Dict)]),
- {ok, _, _} = compile:file(Name ++ ".erl", [return]).
-
-opts(M)
- when M == "rfc4006_cc";
- M == "rfc4072_eap" ->
- [inherits("rfc4005_nas")];
-opts("rfc4740_sip") ->
- [inherits("rfc4590_digest")];
-opts(_) ->
- [].
-
-inherits(File) ->
- {Name, _} = make_name(File),
- {inherits, File ++ "/" ++ Name}.
-
-make_name(File) ->
- {R, [$_|N]} = lists:splitwith(fun(C) -> C /= $_ end, File),
- {string:join(["diameter_gen", N, R], "_"), "diameter_" ++ N}.
-
-%% ===========================================================================
modify(Bin, Mods) ->
lists:foldl(fun re/2, Bin, Mods).
diff --git a/lib/diameter/test/diameter_config_SUITE.erl b/lib/diameter/test/diameter_config_SUITE.erl
new file mode 100644
index 0000000000..46ff63756d
--- /dev/null
+++ b/lib/diameter/test/diameter_config_SUITE.erl
@@ -0,0 +1,272 @@
+%% coding: utf-8
+%%
+%% %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%
+%%
+
+%%
+%% Test service and transport config. In particular, of the detection
+%% of config errors.
+%%
+
+-module(diameter_config_SUITE).
+
+-export([suite/0,
+ all/0]).
+
+%% testcases
+-export([start/1,
+ start_service/1,
+ add_transport/1,
+ stop/1]).
+
+-define(util, diameter_util).
+
+%% Lists of {Key, GoodConfigList, BadConfigList} with which to
+%% configure.
+
+-define(SERVICE_CONFIG,
+ [{application,
+ [[[{dictionary, diameter_gen_base_rfc6733},
+ {module, ?MODULE}]]
+ | [[[{dictionary, D},
+ {module, M},
+ {alias, A},
+ {state, S},
+ {answer_errors, AE},
+ {request_errors, RE},
+ {call_mutates_state, C}]]
+ || D <- [diameter_gen_base_rfc3588, diameter_gen_base_rfc6733],
+ M <- [?MODULE, [?MODULE, now()]],
+ A <- [0, common, make_ref()],
+ S <- [[], make_ref()],
+ AE <- [report, callback, discard],
+ RE <- [answer_3xxx, answer, callback],
+ C <- [true, false]]],
+ [[x],
+ [[]],
+ [[{dictionary, diameter_gen_base_rfc3588}]],
+ [[{module, ?MODULE}]]
+ | [[[{dictionary, diameter_gen_base_rfc6733},
+ {module, ?MODULE},
+ {K,x}]]
+ || K <- [answer_errors,
+ request_errors,
+ call_mutates_state]]]},
+ {restrict_connections,
+ [[false], [node], [nodes], [[node(), node()]]],
+ []},
+ {sequence,
+ [[{0,32}], [{1,31}]],
+ [[{2,31}]]},
+ {share_peers,
+ [[true],
+ [false],
+ [[node()]]],
+ [[x]]},
+ {use_shared_peers,
+ [[true],
+ [false],
+ [[node(), node()]]],
+ [[x]]},
+ {invalid_option, %% invalid service options are rejected
+ [],
+ [[x],
+ [x,x]]}]).
+
+-define(TRANSPORT_CONFIG,
+ [{transport_module,
+ [[?MODULE]],
+ [[[?MODULE]]]},
+ {transport_config,
+ [[{}, 3000],
+ [{}, infinity]],
+ [[{}, x]]},
+ {applications,
+ [[[1, a, [x]]]],
+ [[x]]},
+ {capabilities,
+ [[[{'Origin-Host', "diameter.erlang.org"}]],
+ [[{'Origin-Realm', "erlang.org"}]]]
+ ++ [[[{'Host-IP-Address', L}]]
+ || L <- [[{127,0,0,1}],
+ ["127.0.0.1"],
+ ["127.0.0.1", "FFFF::1", "::1", {1,2,3,4,5,6,7,8}]]]
+ ++ [[[{'Product-Name', N}]]
+ || N <- [["Product", $-, ["Name"]],
+ "Norðurálfa",
+ "ᚠᚢᚦᚨᚱᚲ"]]
+ ++ [[[{K,V}]]
+ || K <- ['Vendor-Id',
+ 'Origin-State-Id',
+ 'Firmware-Revision'],
+ V <- [0, 256, 16#FFFF]]
+ ++ [[[{K,V}]]
+ || K <- ['Supported-Vendor-Id',
+ 'Auth-Application-Id',
+ 'Acct-Application-Id',
+ 'Inband-Security-Id'],
+ V <- [[17], [0, 256, 16#FFFF]]]
+ ++ [[[{'Vendor-Specific-Application-Id',
+ [[{'Vendor-Id', V},
+ {'Auth-Application-Id', [0]},
+ {'Acct-Application-Id', [4]}]]}]]
+ || V <- [1, [1]]],
+ [[x], [[{'Origin-Host', "ᚠᚢᚦᚨᚱᚲ"}]]]
+ ++ [[[{'Host-IP-Address', A}]]
+ || A <- [{127,0,0,1}]]
+ ++ [[[{'Product-Name', N}]]
+ || N <- [x, 1]]
+ ++ [[[{K,V}]]
+ || K <- ['Vendor-Id',
+ 'Origin-State-Id',
+ 'Firmware-Revision'],
+ V <- [x, [0], -1, 1 bsl 32]]
+ ++ [[[{K,V}]]
+ || K <- ['Supported-Vendor-Id',
+ 'Auth-Application-Id',
+ 'Acct-Application-Id',
+ 'Inband-Security-Id'],
+ V <- [x, 17, [-1], [1 bsl 32]]]
+ ++ [[[{'Vendor-Specific-Application-Id', V}]]
+ || V <- [x,
+ [[{'Vendor-Id', 1 bsl 32}]],
+ [[{'Auth-Application-Id', 1}]]]]},
+ {capabilities_cb,
+ [[x]],
+ []},
+ {capx_timeout,
+ [[3000]],
+ [[{?MODULE, tmo, []}]]},
+ {disconnect_cb,
+ [[x]],
+ []},
+ {length_errors,
+ [[exit], [handle], [discard]],
+ [[x]]},
+ {reconnect_timer,
+ [[3000]],
+ [[infinity]]},
+ {watchdog_timer,
+ [[3000],
+ [{?MODULE, tmo, []}]],
+ [[infinity],
+ [-1]]},
+ {watchdog_config,
+ [[[{okay, 0}, {suspect, 0}]],
+ [[{okay, 1}]],
+ [[{suspect, 2}]]],
+ [[x],
+ [[{open, 0}]]]},
+ {private,
+ [[x]],
+ []},
+ {invalid_option, %% invalid transport options are silently ignored
+ [[x],
+ [x,x]],
+ []}]).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [start,
+ start_service,
+ add_transport,
+ stop].
+
+%% ===========================================================================
+
+start(_) ->
+ ok = diameter:start().
+
+start_service(T)
+ when is_tuple(T) ->
+ do(fun start/3, T);
+
+start_service(_) ->
+ [] = ?util:run([{?MODULE, start_service, [T]}
+ || T <- [lists:keyfind(capabilities, 1, ?TRANSPORT_CONFIG)
+ | ?SERVICE_CONFIG]]).
+
+add_transport(T)
+ when is_tuple(T) ->
+ do(fun add/3, T);
+
+add_transport(_) ->
+ [] = ?util:run([{?MODULE, add_transport, [T]}
+ || T <- ?TRANSPORT_CONFIG]).
+
+stop(_) ->
+ ok = diameter:stop().
+
+%% ===========================================================================
+
+%% do/2
+
+do(F, {Key, Good, Bad}) ->
+ F(Key, Good, Bad).
+
+%% add/3
+
+add(Key, Good, Bad) ->
+ {[],[]} = {[{Vs,T} || Vs <- Good,
+ T <- [add(Key, Vs)],
+ [T] /= [T || {ok,_} <- [T]]],
+ [{Vs,T} || Vs <- Bad,
+ T <- [add(Key, Vs)],
+ [T] /= [T || {error,_} <- [T]]]}.
+
+add(Key, Vs) ->
+ T = list_to_tuple([Key | Vs]),
+ diameter:add_transport(make_ref(), {connect, [T]}).
+
+%% start/3
+
+start(Key, Good, Bad) ->
+ {[],[]} = {[{Vs,T} || Vs <- Good,
+ T <- [start(Key, Vs)],
+ T /= ok],
+ [{Vs,T} || Vs <- Bad,
+ T <- [start(Key, Vs)],
+ [T] /= [T || {error,_} <- [T]]]}.
+
+start(capabilities = K, [Vs]) ->
+ if is_list(Vs) ->
+ start(make_ref(), Vs ++ apps(K));
+ true ->
+ {error, Vs}
+ end;
+
+start(Key, Vs)
+ when is_atom(Key) ->
+ start(make_ref(), [list_to_tuple([Key | Vs]) | apps(Key)]);
+
+start(SvcName, Opts) ->
+ try
+ diameter:start_service(SvcName, Opts)
+ after
+ diameter:stop_service(SvcName)
+ end.
+
+apps(application) ->
+ [];
+apps(_) ->
+ [{application, [{dictionary, diameter_gen_base_rfc6733},
+ {module, ?MODULE}]}].
diff --git a/lib/diameter/test/diameter_ct.erl b/lib/diameter/test/diameter_ct.erl
index 1697287a22..ed2f884681 100644
--- a/lib/diameter/test/diameter_ct.erl
+++ b/lib/diameter/test/diameter_ct.erl
@@ -53,7 +53,7 @@ info(L0, L1) ->
L0,
L1),
Diff = [T, C, {memory, M}],
- ct:pal("INFO: ~p~n", [Diff]).
+ io:format("INFO: ~p~n", [Diff]).
diff(time, T0, T1) ->
timer:now_diff(T1, T0);
diff --git a/lib/diameter/test/diameter_distribution_SUITE.erl b/lib/diameter/test/diameter_distribution_SUITE.erl
new file mode 100644
index 0000000000..01d3507b27
--- /dev/null
+++ b/lib/diameter/test/diameter_distribution_SUITE.erl
@@ -0,0 +1,372 @@
+%%
+%% %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 traffic between two Diameter nodes, the client being
+%% spread across three Erlang nodes.
+%%
+
+-module(diameter_distribution_SUITE).
+
+-export([suite/0,
+ all/0]).
+
+%% testcases
+-export([enslave/1,
+ ping/1,
+ start/1,
+ connect/1,
+ send_local/1,
+ send_remote/1,
+ send_timeout/1,
+ send_failover/1,
+ stop/1]).
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/5,
+ prepare_request/4,
+ prepare_retransmit/4,
+ handle_answer/5,
+ handle_error/5,
+ handle_request/3]).
+
+-export([call/1]).
+
+-include("diameter.hrl").
+-include("diameter_gen_base_rfc6733.hrl").
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+
+-define(CLIENT, 'CLIENT').
+-define(SERVER, 'SERVER').
+-define(REALM, "erlang.org").
+-define(DICT, diameter_gen_base_rfc6733).
+-define(ADDR, {127,0,0,1}).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Host),
+ [{'Origin-Host', Host ++ [$.|?REALM]},
+ {'Origin-Realm', ?REALM},
+ {'Host-IP-Address', [?ADDR]},
+ {'Vendor-Id', 12345},
+ {'Product-Name', "OTP/diameter"},
+ {'Auth-Application-Id', [?DICT:id()]},
+ {'Origin-State-Id', origin()},
+ {share_peers, peers()},
+ {use_shared_peers, peers()},
+ {restrict_connections, false},
+ {sequence, fun sequence/0},
+ {application, [{dictionary, ?DICT},
+ {module, ?MODULE},
+ {request_errors, callback},
+ {answer_errors, callback}]}]).
+
+-define(SUCCESS, 2001).
+-define(BUSY, 3004).
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
+-define(MOVED, ?'DIAMETER_BASE_TERMINATION-CAUSE_USER_MOVED').
+-define(TIMEOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_SESSION_TIMEOUT').
+
+-define(L, atom_to_list).
+-define(A, list_to_atom).
+
+%% The order here is significant and causes the server to listen
+%% before the clients connect.
+-define(NODES, [{server, ?SERVER},
+ {client0, ?CLIENT},
+ {client1, ?CLIENT},
+ {client2, ?CLIENT}]).
+
+%% Options to ct_slave:start/2.
+-define(TIMEOUTS, [{T, 15000} || T <- [boot_timeout,
+ init_timeout,
+ start_timeout]]).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [enslave,
+ ping,
+ start,
+ connect,
+ send_local,
+ send_remote,
+ send_timeout,
+ send_failover,
+ stop].
+
+%% ===========================================================================
+%% start/stop testcases
+
+%% enslave/1
+%%
+%% Start four slave nodes, one to implement a Diameter server,
+%% two three to implement a client.
+
+enslave(Config) ->
+ Here = filename:dirname(code:which(?MODULE)),
+ Ebin = filename:join([Here, "..", "ebin"]),
+ Dirs = [Here, Ebin],
+ Nodes = [{N,S} || {M,S} <- ?NODES, N <- [slave(M, Dirs)]],
+ ?util:write_priv(Config, nodes, [{N,S} || {{N,ok},S} <- Nodes]),
+ [] = [{T,S} || {{_,E} = T, S} <- Nodes, E /= ok].
+
+slave(Name, Dirs) ->
+ add_pathsa(Dirs, ct_slave:start(Name, ?TIMEOUTS)).
+
+add_pathsa(Dirs, {ok, Node}) ->
+ {Node, rpc:call(Node, code, add_pathsa, [Dirs])};
+add_pathsa(_, No) ->
+ {No, error}.
+
+%% ping/1
+%%
+%% Ensure the client nodes are connected since the sharing of
+%% transports is only between connected nodes.
+
+ping({?SERVER, _Nodes}) ->
+ [];
+
+ping({?CLIENT, Nodes}) ->
+ [N || {N,_} <- Nodes,
+ node() /= N,
+ pang <- [net_adm:ping(N)]];
+
+ping(Config) ->
+ Nodes = ?util:read_priv(Config, nodes),
+ [] = [{N,RC} || {N,S} <- Nodes,
+ RC <- [rpc:call(N, ?MODULE, ping, [{S, Nodes}])],
+ RC /= []].
+
+%% start/1
+%%
+%% Start diameter services.
+
+start(SvcName)
+ when is_atom(SvcName) ->
+ ok = diameter:start(),
+ ok = diameter:start_service(SvcName, ?SERVICE((?L(SvcName))));
+
+start(Config) ->
+ Nodes = ?util:read_priv(Config, nodes),
+ [] = [{N,RC} || {N,S} <- Nodes,
+ RC <- [rpc:call(N, ?MODULE, start, [S])],
+ RC /= ok].
+
+sequence() ->
+ sequence(sname()).
+
+sequence(server) ->
+ {0,32};
+sequence(Client) ->
+ "client" ++ N = ?L(Client),
+ {list_to_integer(N), 30}.
+
+origin() ->
+ origin(sname()).
+
+origin(server) ->
+ 99;
+origin(Client) ->
+ "client" ++ N = ?L(Client),
+ list_to_integer(N).
+
+peers() ->
+ peers(sname()).
+
+peers(server) -> true;
+peers(client0) -> [node() | nodes()];
+peers(client1) -> fun erlang:nodes/0;
+peers(client2) -> nodes().
+
+%% connect/1
+%%
+%% Establish one connection to the server from each of the client
+%% nodes.
+
+connect({?SERVER, Config}) ->
+ ?util:write_priv(Config, lref, {node(), ?util:listen(?SERVER, tcp)}),
+ ok;
+
+connect({?CLIENT, Config}) ->
+ ?util:connect(?CLIENT, tcp, ?util:read_priv(Config, lref)),
+ ok;
+
+connect(Config) ->
+ Nodes = ?util:read_priv(Config, nodes),
+ [] = [{N,RC} || {N,S} <- Nodes,
+ RC <- [rpc:call(N, ?MODULE, connect, [{S,Config}])],
+ RC /= ok].
+
+%% stop/1
+%%
+%% Stop the slave nodes.
+
+stop(_Config) ->
+ [] = [{N,E} || {N,_} <- ?NODES,
+ {error, _, _} = E <- [ct_slave:stop(N)]].
+
+%% ===========================================================================
+%% traffic testcases
+
+%% send_local/1
+%%
+%% Send a request from the first client node, using a the local
+%% transport.
+
+send_local(Config) ->
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = send(Config, local, str(?LOGOUT)).
+
+%% send_remote/1
+%%
+%% Send a request from the first client node, using a transport on the
+%% another node.
+
+send_remote(Config) ->
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = send(Config, remote, str(?LOGOUT)).
+
+%% send_timeout/1
+%%
+%% Send a request that the server discards.
+
+send_timeout(Config) ->
+ {error, timeout} = send(Config, remote, str(?TIMEOUT)).
+
+%% send_failover/1
+%%
+%% Send a request that causes the server to remote transports down.
+
+send_failover(Config) ->
+ #'diameter_base_answer-message'{'Result-Code' = ?BUSY}
+ = send(Config, remote, str(?MOVED)).
+
+%% ===========================================================================
+
+str(Cause) ->
+ #diameter_base_STR{'Destination-Realm' = ?REALM,
+ 'Auth-Application-Id' = ?DICT:id(),
+ 'Termination-Cause' = Cause}.
+
+%% send/2
+
+send(Config, Where, Req) ->
+ [_, {Node, _} | _] = ?util:read_priv(Config, nodes) ,
+ rpc:call(Node, ?MODULE, call, [{Where, Req}]).
+
+%% call/1
+
+call({Where, Req}) ->
+ diameter:call(?CLIENT, ?DICT, Req, [{extra, [{Where, sname()}]}]).
+
+%% sname/0
+
+sname() ->
+ ?A(hd(string:tokens(?L(node()), "@"))).
+
+%% ===========================================================================
+%% diameter callbacks
+
+%% peer_up/3
+
+peer_up(_SvcName, _Peer, State) ->
+ State.
+
+%% peer_down/3
+
+peer_down(_SvcName, _Peer, State) ->
+ State.
+
+%% pick_peer/4
+
+pick_peer([LP], [_, _], ?CLIENT, _State, {local, client0}) ->
+ {ok, LP};
+
+pick_peer([_], [RP | _], ?CLIENT, _State, {remote, client0}) ->
+ {ok, RP};
+
+pick_peer([LP], [], ?CLIENT, _State, {remote, client0}) ->
+ {ok, LP}.
+
+%% prepare_request/4
+
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, {_, client0}) ->
+ #diameter_packet{msg = Req}
+ = Pkt,
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}
+ = Caps,
+ {send, Req#diameter_base_STR{'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Session-Id' = diameter:session_id(OH)}}.
+
+prepare_retransmit(Pkt, ?CLIENT, _, {_, client0}) ->
+ #diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = ?MOVED}}
+ = Pkt, %% assert
+ {send, Pkt}.
+
+%% handle_answer/5
+
+handle_answer(Pkt, _Req, ?CLIENT, _Peer, {_, client0}) ->
+ #diameter_packet{msg = Rec, errors = []} = Pkt,
+ Rec.
+
+%% handle_error/5
+
+handle_error(Reason, _Req, ?CLIENT, _Peer, {_, client0}) ->
+ {error, Reason}.
+
+%% handle_request/3
+
+handle_request(Pkt, ?SERVER, Peer) ->
+ server = sname(), %% assert
+ #diameter_packet{msg = Req}
+ = Pkt,
+ request(Req, Peer).
+
+request(#diameter_base_STR{'Termination-Cause' = ?TIMEOUT}, _) ->
+ discard;
+
+request(#diameter_base_STR{'Termination-Cause' = ?MOVED}, Peer) ->
+ {TPid, #diameter_caps{origin_state_id = {_, [N]}}} = Peer,
+ fail(N, TPid);
+
+request(#diameter_base_STR{'Session-Id' = SId}, {_, Caps}) ->
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, _}}
+ = Caps,
+ {reply, #diameter_base_STA{'Result-Code' = ?SUCCESS,
+ 'Session-Id' = SId,
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR}}.
+
+fail(0, _) -> %% sent from the originating node ...
+ {protocol_error, ?BUSY};
+
+fail(_, TPid) -> %% ... or through a remote node: force failover
+ exit(TPid, kill),
+ discard.
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
index 18bdcb1f54..94b4967921 100644
--- a/lib/diameter/test/diameter_event_SUITE.erl
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -100,7 +100,7 @@ start_server(Config) ->
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),
+ [PortNr] = ?util:lport(tcp, LRef),
?util:write_priv(Config, portnr, PortNr),
start = event(?SERVER).
diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl
new file mode 100644
index 0000000000..585fc9d3b8
--- /dev/null
+++ b/lib/diameter/test/diameter_examples_SUITE.erl
@@ -0,0 +1,334 @@
+%%
+%% %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%
+%%
+
+%%
+%% Test example code under ../examples/code.
+%%
+
+-module(diameter_examples_SUITE).
+
+-export([suite/0,
+ all/0]).
+
+%% testcases
+-export([dict/1, dict/0,
+ code/1,
+ enslave/1,
+ start/1,
+ traffic/1,
+ stop/1]).
+
+-export([install/1,
+ call/1]).
+
+-include("diameter.hrl").
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+
+%% The order here is significant and causes the server to listen
+%% before the clients connect.
+-define(NODES, [compile, server, client]).
+
+%% Options to ct_slave:start/2.
+-define(TIMEOUTS, [{T, 15000} || T <- [boot_timeout,
+ init_timeout,
+ start_timeout]]).
+
+%% @inherits dependencies between example dictionaries. This is needed
+%% in order compile them in the right order. Can't compile to erl to
+%% find out since @inherits is a beam dependency.
+-define(INHERITS, [{rfc4006_cc, [rfc4005_nas]},
+ {rfc4072_eap, [rfc4005_nas]},
+ {rfc4740_sip, [rfc4590_digest]}]).
+
+%% Common dictionaries to inherit from examples.
+-define(DICT0, [rfc3588_base, rfc6733_base]).
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 45}}].
+
+all() ->
+ [dict,
+ code,
+ enslave,
+ start,
+ traffic,
+ stop].
+
+%% ===========================================================================
+%% dict/1
+%%
+%% Compile example dictionaries in examples/dict.
+
+dict() ->
+ [{timetrap, {minutes, 10}}].
+
+dict(_Config) ->
+ Dirs = [filename:join(H ++ ["examples", "dict"])
+ || H <- [[code:lib_dir(diameter)], [here(), ".."]]],
+ [] = [{F,D,RC} || {_,F} <- sort(find_files(Dirs, ".*\\.dia")),
+ D <- ?DICT0,
+ RC <- [make(F,D)],
+ RC /= ok].
+
+sort([{_,_} | _] = Files) ->
+ lists:sort(fun({A,_},{B,_}) ->
+ sort([filename:rootname(F) || F <- [A,B]])
+ end,
+ Files);
+
+sort([A,B] = L) ->
+ [DA,DB] = [dep([D],[]) || D <- L],
+ case {[A] -- DB, [B] -- DA} of
+ {[], [_]} -> %% B depends on A
+ true;
+ {[_], []} -> %% A depends on B
+ false;
+ {[_],[_]} -> %% or not
+ length(DA) < length(DB)
+ end.
+
+%% Recursively accumulate inherited dictionaries.
+dep([D|Rest], Acc) ->
+ dep(dep(D), Rest, Acc);
+dep([], Acc) ->
+ Acc.
+
+dep([{Dict, _} | T], Rest, Acc) ->
+ dep(T, [Dict | Rest], [Dict | Acc]);
+dep([], Rest, Acc) ->
+ dep(Rest, Acc).
+
+make(Path, Dict0)
+ when is_atom(Dict0) ->
+ make(Path, atom_to_list(Dict0));
+
+make(Path, Dict0) ->
+ Dict = filename:rootname(filename:basename(Path)),
+ {Mod, Pre} = make_name(Dict),
+ {"diameter_gen_base" ++ Suf = Mod0, _} = make_name(Dict0),
+ Name = Mod ++ Suf,
+ try
+ ok = to_erl(Path, [{name, Name},
+ {prefix, Pre},
+ {inherits, "rfc3588_base/" ++ Mod0}
+ | [{inherits, D ++ "/" ++ M ++ Suf}
+ || {D,M} <- dep(Dict)]]),
+ ok = to_beam(Name)
+ catch
+ throw: {_,_} = E ->
+ E
+ end.
+
+to_erl(File, Opts) ->
+ case diameter_make:codec(File, Opts) of
+ ok ->
+ ok;
+ No ->
+ throw({make, No})
+ end.
+
+to_beam(Name) ->
+ case compile:file(Name ++ ".erl", [return]) of
+ {ok, _, _} ->
+ ok;
+ No ->
+ throw({compile, No})
+ end.
+
+dep(Dict) ->
+ case lists:keyfind(list_to_atom(Dict), 1, ?INHERITS) of
+ {_, Is} ->
+ lists:map(fun inherits/1, Is);
+ false ->
+ []
+ end.
+
+inherits(Dict)
+ when is_atom(Dict) ->
+ inherits(atom_to_list(Dict));
+
+inherits(Dict) ->
+ {Name, _} = make_name(Dict),
+ {Dict, Name}.
+
+make_name(Dict) ->
+ {R, [$_|N]} = lists:splitwith(fun(C) -> C /= $_ end, Dict),
+ {string:join(["diameter_gen", N, R], "_"), "diameter_" ++ N}.
+
+%% ===========================================================================
+%% code/1
+%%
+%% Compile example code under examples/code.
+
+code(Config) ->
+ Node = slave(hd(?NODES), here()),
+ [] = rpc:call(Node,
+ ?MODULE,
+ install,
+ [proplists:get_value(priv_dir, Config)]).
+
+%% Compile on another node since the code path may be modified.
+install(PrivDir) ->
+ Top = install(here(), PrivDir),
+ Src = filename:join([Top, "examples", "code"]),
+ Files = find_files([Src], ".*\\.erl"),
+ [] = [{F,E} || {_,F} <- Files,
+ {error, _, _} = E <- [compile:file(F, [warnings_as_errors,
+ return_errors])]].
+
+%% Copy include files into a temporary directory and adjust the code
+%% path in order for example code to be able to include them with
+%% include_lib. This is really only required when running in the reop
+%% since generated includes, that the example code wants to
+%% include_lib, are under src/gen and there's no way to get get the
+%% preprocessor to find these otherwise. Generated hrls are only be
+%% under include in an installation. ("Installing" them locally is
+%% anathema.)
+install(Dir, PrivDir) ->
+ %% Remove the path added by slave/1 (needed for the rpc:call/4 in
+ %% compile/1 to find ?MODULE) so the call to code:lib_dir/2 below
+ %% returns the installed path.
+ [Ebin | _] = code:get_path(),
+ true = code:del_path(Ebin),
+ Top = top(Dir, code:lib_dir(diameter)),
+
+ %% Create a new diameter/include in priv_dir. Copy all includes
+ %% there, from below ../include and ../src/gen if they exist (in
+ %% the repo).
+ Tmp = filename:join([PrivDir, "diameter"]),
+ TmpInc = filename:join([PrivDir, "diameter", "include"]),
+ TmpEbin = filename:join([PrivDir, "diameter", "ebin"]),
+ [] = [{T,E} || T <- [Tmp, TmpInc, TmpEbin],
+ {error, E} <- [file:make_dir(T)]],
+
+ Inc = filename:join([Top, "include"]),
+ Gen = filename:join([Top, "src", "gen"]),
+ Files = find_files([Inc, Gen], ".*\\.hrl"),
+ [] = [{F,E} || {_,F} <- Files,
+ B <- [filename:basename(F)],
+ D <- [filename:join([TmpInc, B])],
+ {error, E} <- [file:copy(F,D)]],
+
+ %% Prepend the created directory just so that code:lib_dir/1 finds
+ %% it when compile:file/2 tries to resolve include_lib.
+ true = code:add_patha(TmpEbin),
+ Tmp = code:lib_dir(diameter), %% assert
+ %% Return the top directory containing examples/code.
+ Top.
+
+find_files(Dirs, RE) ->
+ lists:foldl(fun(D,A) -> fold_files(D, RE, A) end,
+ orddict:new(),
+ Dirs).
+
+fold_files(Dir, RE, Acc) ->
+ filelib:fold_files(Dir, RE, false, fun store/2, Acc).
+
+store(Path, Dict) ->
+ orddict:store(filename:basename(Path), Path, Dict).
+
+%% ===========================================================================
+
+%% enslave/1
+%%
+%% Start two nodes: one for the server, one for the client.
+
+enslave(Config) ->
+ Dir = here(),
+ Nodes = [{N, slave(N, Dir)} || N <- tl(?NODES)],
+ ?util:write_priv(Config, nodes, Nodes).
+
+slave(Name, Dir) ->
+ {ok, Node} = ct_slave:start(Name, ?TIMEOUTS),
+ ok = rpc:call(Node,
+ code,
+ add_pathsa,
+ [[Dir, filename:join([Dir, "..", "ebin"])]]),
+ Node.
+
+here() ->
+ filename:dirname(code:which(?MODULE)).
+
+top(Dir, LibDir) ->
+ File = filename:join([Dir, "depend.sed"]), %% only in the repo
+ case filelib:is_regular(File) of
+ true -> filename:join([Dir, ".."]);
+ false -> LibDir
+ end.
+
+%% start/1
+
+start(server) ->
+ ok = diameter:start(),
+ ok = server:start(),
+ {ok, Ref} = server:listen(tcp),
+ [_] = ?util:lport(tcp, Ref),
+ ok;
+
+start(client) ->
+ ok = diameter:start(),
+ true = diameter:subscribe(client),
+ ok = client:start(),
+ {ok, Ref} = client:connect(tcp),
+ receive #diameter_event{info = {up, Ref, _, _, _}} -> ok end;
+
+start(Config) ->
+ Nodes = ?util:read_priv(Config, nodes),
+ [] = [RC || {T,N} <- Nodes,
+ RC <- [rpc:call(N, ?MODULE, start, [T])],
+ RC /= ok].
+
+%% traffic/1
+%%
+%% Send successful messages from client to server.
+
+traffic(server) ->
+ ok;
+
+traffic(client) ->
+ {_, MRef} = spawn_monitor(fun() -> call(100) end),
+ receive {'DOWN', MRef, process, _, Reason} -> Reason end;
+
+traffic(Config) ->
+ Nodes = ?util:read_priv(Config, nodes),
+ [] = [RC || {T,N} <- Nodes,
+ RC <- [rpc:call(N, ?MODULE, traffic, [T])],
+ RC /= ok].
+
+call(0) ->
+ exit(ok);
+
+call(N) ->
+ {ok, _} = client:call(),
+ call(N-1).
+
+%% stop/1
+
+stop(Name)
+ when is_atom(Name) ->
+ {ok, _Node} = ct_slave:stop(Name),
+ ok;
+
+stop(_Config) ->
+ [] = [RC || N <- ?NODES, RC <- [stop(N)], RC /= ok].
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 77194a0f56..5a79c63d36 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -343,7 +343,7 @@ join(Strs) ->
server(Host, {Caps, Opts}) ->
ok = diameter:start_service(Host, ?SERVICE(Host, ?DICT_COMMON)),
{ok, LRef} = diameter:add_transport(Host, ?LISTEN(Caps, Opts)),
- {LRef, hd([_] = ?util:lport(tcp, LRef, 20))}.
+ {LRef, hd([_] = ?util:lport(tcp, LRef))}.
sopts(?SERVER1, Dir) ->
{inband_security([?TLS]),
diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl
index 893b7ba2f9..97f4cec11f 100644
--- a/lib/diameter/test/diameter_transport_SUITE.erl
+++ b/lib/diameter/test/diameter_transport_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,6 +36,7 @@
tcp_connect/1,
sctp_accept/1,
sctp_connect/1,
+ reconnect/1, reconnect/0,
stop/1]).
-export([accept/1,
@@ -54,9 +55,6 @@
-define(RECV(Pat, Ret), receive Pat -> Ret end).
-define(RECV(Pat), ?RECV(Pat, now())).
-%% Or not.
--define(WAIT(Ms), receive after Ms -> now() end).
-
%% Sockets are opened on the loopback address.
-define(ADDR, {127,0,0,1}).
@@ -102,7 +100,8 @@ tc() ->
[tcp_accept,
tcp_connect,
sctp_accept,
- sctp_connect].
+ sctp_connect,
+ reconnect].
init_per_suite(Config) ->
[{sctp, have_sctp()} | Config].
@@ -165,6 +164,90 @@ connect(Prot) ->
[] = ?util:run([{?MODULE, [init, X, T]} || X <- [gen_accept, connect]]).
%% ===========================================================================
+%% reconnect/1
+%%
+%% Exercise reconnection behaviour: that a connecting transport
+%% doesn't try to establish a new connection until the old one is
+%% broken.
+
+reconnect() ->
+ [{timetrap, {minutes, 4}}].
+
+reconnect({listen, Ref}) ->
+ SvcName = make_ref(),
+ ok = start_service(SvcName),
+ LRef = ?util:listen(SvcName, tcp, [{watchdog_timer, 6000}]),
+ [_] = diameter_reg:wait({diameter_tcp, listener, {LRef, '_'}}),
+ true = diameter_reg:add_new({?MODULE, Ref, LRef}),
+
+ %% Wait for partner to request transport death: kill to force the
+ %% peer to reconnect.
+ TPid = abort(SvcName, LRef, Ref),
+
+ exit(TPid, kill),
+
+ abort(SvcName, LRef, Ref);
+
+reconnect({connect, Ref}) ->
+ SvcName = make_ref(),
+ true = diameter:subscribe(SvcName),
+ ok = start_service(SvcName),
+ [{{_, _, LRef}, Pid}] = diameter_reg:wait({?MODULE, Ref, '_'}),
+ CRef = ?util:connect(SvcName, tcp, LRef, [{reconnect_timer, 2000},
+ {watchdog_timer, 6000}]),
+
+ %% Tell partner to kill transport after seeing that there are no
+ %% reconnection attempts.
+ abort(SvcName, Pid, Ref),
+
+ %% Transport does down and is reestablished.
+ ?RECV(#diameter_event{service = SvcName, info = {down, CRef, _, _}}),
+ ?RECV(#diameter_event{service = SvcName, info = {reconnect, CRef, _}}),
+ ?RECV(#diameter_event{service = SvcName, info = {up, CRef, _, _, _}}),
+
+ %% Kill again.
+ abort(SvcName, Pid, Ref),
+
+ %% Wait for partner to die.
+ MRef = erlang:monitor(process, Pid),
+ ?RECV({'DOWN', MRef, process, _, _});
+
+reconnect(_) ->
+ Ref = make_ref(),
+ [] = ?util:run([{?MODULE, [reconnect, {T, Ref}]}
+ || T <- [listen, connect]]).
+
+start_service(SvcName) ->
+ OH = io_lib:format("~p-~p-~p", tuple_to_list(now())),
+ Opts = [{application, [{dictionary, diameter_gen_base_rfc6733},
+ {module, diameter_callback}]},
+ {'Origin-Host', OH},
+ {'Origin-Realm', OH ++ ".org"},
+ {'Vendor-Id', 0},
+ {'Product-Name', "x"},
+ {'Auth-Application-Id', [0]}],
+ diameter:start_service(SvcName, Opts).
+
+abort(SvcName, Pid, Ref)
+ when is_pid(Pid) ->
+ receive
+ #diameter_event{service = SvcName, info = {reconnect, _, _}} = E ->
+ erlang:error(E)
+ after 45000 ->
+ ok
+ end,
+ Pid ! {abort, Ref};
+
+abort(SvcName, LRef, Ref)
+ when is_reference(LRef) ->
+ ?RECV({abort, Ref}),
+ [[{ref, LRef}, {type, listen}, {options, _}, {accept, [_,_] = Ts} | _]]
+ %% assert on two accepting
+ = diameter:service_info(SvcName, transport),
+ [TPid] = [P || [{watchdog, {_,_,okay}}, {peer, {P,_}} | _] <- Ts],
+ TPid.
+
+%% ===========================================================================
%% ===========================================================================
%% have_sctp/0
@@ -209,7 +292,7 @@ init(accept, {Prot, Ref}) ->
init(gen_connect, {Prot, Ref}) ->
%% Lookup the peer's listening socket.
- [PortNr] = ?util:lport(Prot, Ref, 20),
+ [PortNr] = ?util:lport(Prot, Ref),
%% Connect, send a message and receive it back.
{ok, Sock} = gen_connect(Prot, PortNr),
@@ -230,7 +313,8 @@ init(gen_accept, {Prot, Ref}) ->
init(connect, {Prot, Ref}) ->
%% Lookup the peer's listening socket.
- [{?TEST_LISTENER(_, PortNr), _}] = match(?TEST_LISTENER(Ref, '_')),
+ [{?TEST_LISTENER(_, PortNr), _}]
+ = diameter_reg:wait(?TEST_LISTENER(Ref, '_')),
%% Start a connecting transport and receive notification of
%% the connection.
@@ -246,18 +330,6 @@ init(connect, {Prot, Ref}) ->
MRef = erlang:monitor(process, TPid),
?RECV({'DOWN', MRef, process, _, _}).
-match(Pat) ->
- match(Pat, 20).
-
-match(Pat, T) ->
- L = diameter_reg:match(Pat),
- if [] /= L orelse 1 == T ->
- L;
- true ->
- ?WAIT(50),
- match(Pat, T-1)
- end.
-
bin(sctp, #diameter_packet{bin = Bin}) ->
Bin;
bin(tcp, Bin) ->
@@ -310,22 +382,18 @@ start_connect(tcp, T, Svc, Opts) ->
%% start_accept/2
%%
%% Start transports sequentially by having each wait for a message
-%% from a job in a queue before commencing. Only one transport with
-%% a pending accept is started at a time since diameter_sctp currently
-%% assumes (and diameter currently implements) this.
+%% from a job in a queue before commencing. Only one transport with a
+%% pending accept is started at a time since diameter_{tcp,sctp}
+%% currently assume (and diameter currently implements) this.
start_accept(Prot, Ref) ->
Pid = sync(accept, Ref),
-
- %% Configure the same port number for transports on the same
- %% reference.
- [PortNr | _] = ?util:lport(Prot, Ref) ++ [0],
{Mod, Opts} = tmod(Prot),
try
{ok, TPid, [?ADDR]} = Mod:start({accept, Ref},
?SVC([?ADDR]),
- [{port, PortNr} | Opts]),
+ [{port, 0} | Opts]),
?RECV(?TMSG({TPid, connected})),
TPid
after
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index 5af4ad9ba5..aa489fef5f 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.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,7 +33,6 @@
%% diameter-specific
-export([lport/2,
- lport/3,
listen/2, listen/3,
connect/3, connect/4,
disconnect/4,
@@ -251,24 +250,17 @@ path(Config, Name) ->
filename:join([Dir, Name]).
%% ---------------------------------------------------------------------------
-%% lport/2-3
+%% lport/2
%%
%% Lookup the port number of a tcp/sctp listening transport.
-lport(M, Ref) ->
- lport(M, Ref, 1).
+lport(M, {Node, Ref}) ->
+ rpc:call(Node, ?MODULE, lport, [M, Ref]);
-lport(M, Ref, Tries) ->
- lp(tmod(M), Ref, Tries).
-
-lp(M, Ref, T) ->
- L = [N || {listen, N, _} <- M:ports(Ref)],
- if [] /= L orelse T =< 1 ->
- L;
- true ->
- receive after 50 -> ok end,
- lp(M, Ref, T-1)
- end.
+lport(Prot, Ref) ->
+ Mod = tmod(Prot),
+ [_] = diameter_reg:wait({'_', listener, {Ref, '_'}}),
+ [N || {listen, N, _} <- Mod:ports(Ref)].
%% ---------------------------------------------------------------------------
%% listen/2-3
@@ -294,7 +286,7 @@ connect(Client, Prot, LRef) ->
connect(Client, Prot, LRef, []).
connect(Client, Prot, LRef, Opts) ->
- [PortNr] = lport(Prot, LRef, 20),
+ [PortNr] = lport(Prot, LRef),
Client = diameter:service_info(Client, name), %% assert
true = diameter:subscribe(Client),
Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index 704bf110c7..b6e8730ec2 100644
--- a/lib/diameter/test/diameter_watchdog_SUITE.erl
+++ b/lib/diameter/test/diameter_watchdog_SUITE.erl
@@ -524,7 +524,7 @@ cfg(listen, _) ->
cfg(connect, Ref) ->
[{{_, _, SvcName}, _Pid}] = diameter_reg:wait({listen, Ref, '_'}),
[[{ref, LRef} | _]] = diameter:service_info(SvcName, transport),
- [LP] = ?util:lport(tcp, LRef, 20),
+ [LP] = ?util:lport(tcp, LRef),
[{raddr, ?ADDR}, {rport, LP}].
%% ===========================================================================
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index c4a713fb10..1a829f8031 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -29,10 +29,13 @@ MODULES = \
diameter_capx_SUITE \
diameter_codec_SUITE \
diameter_codec_test \
+ diameter_config_SUITE \
diameter_compiler_SUITE \
diameter_dict_SUITE \
+ diameter_distribution_SUITE \
diameter_dpr_SUITE \
diameter_event_SUITE \
+ diameter_examples_SUITE \
diameter_failover_SUITE \
diameter_gen_sctp_SUITE \
diameter_length_SUITE \