aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter')
-rwxr-xr-xlib/diameter/bin/diameterc6
-rw-r--r--lib/diameter/doc/src/Makefile1
-rw-r--r--lib/diameter/doc/src/diameter.xml288
-rw-r--r--lib/diameter/doc/src/diameter_app.xml9
-rw-r--r--lib/diameter/doc/src/diameter_codec.xml44
-rw-r--r--lib/diameter/doc/src/diameter_dict.xml19
-rw-r--r--lib/diameter/doc/src/diameter_sctp.xml63
-rw-r--r--lib/diameter/doc/src/diameter_soc.xml1327
-rw-r--r--lib/diameter/doc/src/diameter_soc_rfc6733.xml8693
-rw-r--r--lib/diameter/doc/src/diameter_tcp.xml58
-rw-r--r--lib/diameter/doc/src/files.mk5
-rw-r--r--lib/diameter/doc/src/notes.xml365
-rw-r--r--lib/diameter/doc/src/seealso.ent7
-rw-r--r--lib/diameter/doc/standard/rfc7683.txt2355
-rw-r--r--lib/diameter/examples/code/client.erl10
-rw-r--r--lib/diameter/examples/code/client_cb.erl29
-rw-r--r--lib/diameter/examples/code/node.erl29
-rw-r--r--lib/diameter/include/diameter_gen.hrl722
-rw-r--r--lib/diameter/src/Makefile11
-rw-r--r--lib/diameter/src/app.sed41
-rw-r--r--lib/diameter/src/base/diameter.erl72
-rw-r--r--lib/diameter/src/base/diameter_callback.erl8
-rw-r--r--lib/diameter/src/base/diameter_capx.erl89
-rw-r--r--lib/diameter/src/base/diameter_codec.erl613
-rw-r--r--lib/diameter/src/base/diameter_config.erl325
-rw-r--r--lib/diameter/src/base/diameter_dict.erl154
-rw-r--r--lib/diameter/src/base/diameter_gen.erl951
-rw-r--r--lib/diameter/src/base/diameter_internal.hrl5
-rw-r--r--lib/diameter/src/base/diameter_lib.erl48
-rw-r--r--lib/diameter/src/base/diameter_peer.erl6
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl408
-rw-r--r--lib/diameter/src/base/diameter_reg.erl257
-rw-r--r--lib/diameter/src/base/diameter_service.erl315
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl1545
-rw-r--r--lib/diameter/src/base/diameter_types.erl300
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl184
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl122
-rw-r--r--lib/diameter/src/compiler/diameter_dict_util.erl4
-rw-r--r--lib/diameter/src/compiler/diameter_exprecs.erl6
-rw-r--r--lib/diameter/src/compiler/diameter_make.erl6
-rw-r--r--lib/diameter/src/diameter.app.src17
-rw-r--r--lib/diameter/src/diameter.appup.src22
-rw-r--r--lib/diameter/src/dict/doic_rfc7683.dia50
-rw-r--r--lib/diameter/src/info/diameter_info.erl4
-rw-r--r--lib/diameter/src/modules.mk5
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl428
-rw-r--r--lib/diameter/src/transport/diameter_sctp_sup.erl3
-rw-r--r--lib/diameter/src/transport/diameter_tcp.erl684
-rw-r--r--lib/diameter/test/diameter_app_SUITE.erl14
-rw-r--r--lib/diameter/test/diameter_capx_SUITE.erl6
-rw-r--r--lib/diameter/test/diameter_codec_SUITE.erl20
-rw-r--r--lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl12
-rw-r--r--lib/diameter/test/diameter_codec_test.erl37
-rw-r--r--lib/diameter/test/diameter_compiler_SUITE.erl10
-rw-r--r--lib/diameter/test/diameter_dict_SUITE.erl145
-rw-r--r--lib/diameter/test/diameter_dpr_SUITE.erl113
-rw-r--r--lib/diameter/test/diameter_event_SUITE.erl11
-rw-r--r--lib/diameter/test/diameter_examples_SUITE.erl14
-rw-r--r--lib/diameter/test/diameter_gen_sctp_SUITE.erl455
-rw-r--r--lib/diameter/test/diameter_gen_tcp_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_pool_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_reg_SUITE.erl11
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl4
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl1159
-rw-r--r--lib/diameter/test/diameter_transport_SUITE.erl69
-rw-r--r--lib/diameter/test/diameter_util.erl78
-rw-r--r--lib/diameter/test/diameter_watchdog_SUITE.erl111
-rw-r--r--lib/diameter/test/modules.mk3
-rw-r--r--lib/diameter/vsn.mk4
69 files changed, 9762 insertions, 13235 deletions
diff --git a/lib/diameter/bin/diameterc b/lib/diameter/bin/diameterc
index 3dbd238c19..4d415ece78 100755
--- a/lib/diameter/bin/diameterc
+++ b/lib/diameter/bin/diameterc
@@ -4,7 +4,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -78,8 +78,8 @@ compile(#argv{file = File, options = Opts, output = Out}) ->
error_msg(diameter_make:format_error(Reason), []),
1
catch
- error: Reason ->
- error_msg("ERROR: ~p~n ~p", [Reason, erlang:get_stacktrace()]),
+ error: Reason: Stack ->
+ error_msg("ERROR: ~p~n ~p", [Reason, Stack]),
2
end.
diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile
index 7a7546fc4d..7672598060 100644
--- a/lib/diameter/doc/src/Makefile
+++ b/lib/diameter/doc/src/Makefile
@@ -77,6 +77,7 @@ clean_man:
clean_html:
rm -rf $(HTMLDIR)/*
+ rm -rf $(XMLDIR)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index 72181a42b0..dfa4c803ed 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -21,7 +21,7 @@
<copyright>
<year>2011</year>
-<year>2016</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -135,7 +135,7 @@ along with any
extra arguments to be appended to those documented.
Note that extra arguments specific to an outgoing request can be
specified to &call;, in which case
-those are are appended to any module-specific extra arguments.</p>
+those are appended to any module-specific extra arguments.</p>
<p>
Specifying a <c>#diameter_callback{}</c> record allows individual
@@ -150,7 +150,7 @@ See <c>diameter_callback.erl</c> for details.</p>
<p>
Options defining a Diameter application.
-Has one the following types.</p>
+Has one of the following types.</p>
<taglist>
@@ -300,6 +300,17 @@ corresponding list of filters.
Defaults to <c>none</c>.</p>
</item>
+<tag><c>{peer, &app_peer_ref;}</c></tag>
+<item>
+<p>
+Peer to which the request in question can be sent, preempting the
+selection of peers having advertised support for the Diameter
+application in question.
+Multiple options can be specified, and their order is
+respected in the candidate lists passed to a subsequent
+&app_pick_peer; callback.</p>
+</item>
+
<tag><c>{timeout, &dict_Unsigned32;}</c></tag>
<item>
<p>
@@ -386,10 +397,10 @@ from the peer offers it.</p>
Note that each tuple communicates one or more AVP values.
It is an error to specify duplicate tuples.</p>
-<marker id="evaluable"/>
+<marker id="eval"/>
</item>
-<tag><c>evaluable() = {M,F,A} | fun() | [evaluable() | A]</c></tag>
+<tag><c>eval() = {M,F,A} | fun() | [eval() | A]</c></tag>
<item>
<p>
An expression that can be evaluated as a function in the following
@@ -407,7 +418,7 @@ eval(F) ->
</pre>
<p>
-Applying an <c>&evaluable;</c>
+Applying an <c>&eval;</c>
<c>E</c> to an argument list <c>A</c>
is meant in the sense of <c>eval([E|A])</c>.</p>
@@ -473,11 +484,11 @@ Matches only those peers whose Origin-Realm has the
specified value, or all peers if the atom <c>any</c>.</p>
</item>
-<tag><c>{eval, &evaluable;}</c></tag>
+<tag><c>{eval, &eval;}</c></tag>
<item>
<p>
Matches only those peers for which the specified
-<c>&evaluable;</c> returns
+<c>&eval;</c> returns
<c>true</c> when applied to the connection's <c>diameter_caps</c>
record.
Any other return value or exception is equivalent to <c>false</c>.</p>
@@ -639,7 +650,7 @@ Result = ResultCode | {capabilities_cb, CB, ResultCode|discard}
Caps = #diameter_caps{}
Pkt = #diameter_packet{}
ResultCode = integer()
-CB = &evaluable;
+CB = &eval;
</pre>
<p>
@@ -787,15 +798,31 @@ be matched by corresponding &capability; configuration, of
</item>
<tag>
-<marker id="incoming_maxlen"/><c>{incoming_maxlen, 0..16777215}</c></tag>
+<marker id="decode_format"/>
+<c>{decode_format, record | list | map | none}</c></tag>
<item>
<p>
-Bound on the expected size of incoming Diameter messages.
-Messages larger than the specified number of bytes are discarded.</p>
+The format of decoded messages and grouped AVPs in the <c>msg</c> field
+of diameter_packet records and <c>value</c> field of diameter_avp
+records respectively.
+If <c>record</c> then a record whose definition is generated from the
+dictionary file in question.
+If <c>list</c> or <c>map</c> then a <c>[Name | Avps]</c> pair where
+<c>Avps</c> is a list of AVP name/values pairs or a map keyed on
+AVP names respectively.
+If <c>none</c> then the atom-value message name, or <c>undefined</c>
+for a Grouped AVP.
+See also &codec_message;.</p>
<p>
-Defaults to <c>16777215</c>, the maximum value of the 24-bit Message
-Length field in a Diameter Header.</p>
+Defaults to <c>record</c>.</p>
+
+<note>
+<p>
+AVPs are decoded into a list of diameter_avp records in <c>avps</c>
+field of diameter_packet records independently of
+<c>decode_format</c>.</p>
+</note>
</item>
@@ -803,7 +830,7 @@ Length field in a Diameter Header.</p>
| node
| nodes
| [node()]
- | evaluable()}</c></tag>
+ | eval()}</c></tag>
<item>
<p>
The degree to which the service allows multiple transport
@@ -814,7 +841,7 @@ at capabilities exchange.</p>
If <c>[node()]</c> then a connection is rejected if another already
exists on any of the specified nodes.
Types <c>false</c>, <c>node</c>, <c>nodes</c> and
-&evaluable; are equivalent to
+&eval; are equivalent to
<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.
@@ -829,7 +856,7 @@ by their own peer and watchdog state machines.</p>
Defaults to <c>nodes</c>.</p>
</item>
-<tag><c>{sequence, {H,N} | &evaluable;}</c></tag>
+<tag><c>{sequence, {H,N} | &eval;}</c></tag>
<item>
<p>
A constant value <c>H</c> for the topmost <c>32-N</c> bits of
@@ -864,7 +891,7 @@ outgoing requests.</p>
</warning>
</item>
-<tag><c>{share_peers, boolean() | [node()] | evaluable()}</c></tag>
+<tag><c>{share_peers, boolean() | [node()] | eval()}</c></tag>
<item>
<p>
Nodes to which peer connections established on the local
@@ -877,7 +904,7 @@ configured to use them: see <c>use_shared_peers</c> below.</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
+If <c>eval()</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.
@@ -903,59 +930,36 @@ of a single Diameter node across multiple Erlang nodes.</p>
</note>
</item>
-<tag><c>{spawn_opt, [term()]}</c></tag>
-<item>
-<p>
-Options list passed to &spawn_opt; when spawning a process for an
-incoming Diameter request, unless the transport in question
-specifies another value.
-Options <c>monitor</c> and <c>link</c> are ignored.</p>
-
-<p>
-Defaults to the empty list.</p>
-</item>
-
<tag>
-<marker id="strict_mbit"/><c>{strict_mbit, boolean()}</c></tag>
+<marker id="strict_arities"/><c>{strict_arities, boolean()
+ | encode
+ | decode}</c></tag>
<item>
<p>
-Whether or not to regard an AVP setting the M-bit as erroneous when
-the command grammar in question does not explicitly allow the AVP.
-If <c>true</c> then such AVPs are regarded as 5001 errors,
-DIAMETER_AVP_UNSUPPORTED.
-If <c>false</c> then the M-bit is ignored and policing
-it becomes the receiver's responsibility.</p>
+Whether or not to require that the number of AVPs in a message or
+grouped AVP agree with those specified in the dictionary in question
+when passing messages to &man_app; callbacks.
+If <c>true</c> then mismatches in an outgoing messages cause message
+encoding to fail, while mismatches in an incoming message are reported
+as 5005/5009 errors in the errors field of the diameter_packet record
+passed to &app_handle_request; or &app_handle_answer; callbacks.
+If <c>false</c> then neither error is enforced/detected.
+If <c>encode</c> or <c>decode</c> then errors are only
+enforced/detected on outgoing or incoming messages respectively.</p>
<p>
Defaults to <c>true</c>.</p>
-<warning>
-<p>
-RFC 6733 is unclear about the semantics of the M-bit.
-One the one hand, the CCF specification in section 3.2 documents AVP
-in a command grammar as meaning <em>any</em> arbitrary AVP; on the
-other hand, 1.3.4 states that AVPs setting the M-bit cannot be added
-to an existing command: the modified command must instead be
-placed in a new Diameter application.</p>
-<p>
-The reason for the latter is presumably interoperability:
-allowing arbitrary AVPs setting the M-bit in a command makes its
-interpretation implementation-dependent, since there's no
-guarantee that all implementations will understand the same set of
-arbitrary AVPs in the context of a given command.
-However, interpreting <c>AVP</c> in a command grammar as any
-AVP, regardless of M-bit, renders 1.3.4 meaningless, since the receiver
-can simply ignore any AVP it thinks isn't relevant, regardless of the
-sender's intent.</p>
+<note>
<p>
-Beware of confusing mandatory in the sense of the M-bit with mandatory
-in the sense of the command grammar.
-The former is a semantic requirement: that the receiver understand the
-semantics of the AVP in the context in question.
-The latter is a syntactic requirement: whether or not the AVP must
-occur in the message in question.</p>
-</warning>
-
+Disabling arity checks affects the form of messages at encode/decode.
+In particular, decoded AVPs are represented as lists of values,
+regardless of the AVP's arity (ie. expected number in the message/AVP
+grammar in question), and values are expected to be supplied as lists
+at encode.
+This differs from the historic decode behaviour of representing AVPs
+of arity 1 as bare values, not wrapped in a list.</p>
+</note>
</item>
<tag>
@@ -982,7 +986,27 @@ The default value is for backwards compatibility.</p>
</item>
-<tag><c>{use_shared_peers, boolean() | [node()] | evaluable()}</c></tag>
+<tag>
+<marker id="traffic_counters"/><c>{traffic_counters, boolean()}</c></tag>
+<item>
+<p>
+Whether or not to count application-specific messages; those for which
+&man_app; callbacks take place.
+If false then only messages handled by diameter itself are counted:
+CER/CEA, DWR/DWA, DPR/DPA.</p>
+
+<p>
+Defaults to <c>true</c>.</p>
+
+<note>
+<p>
+Disabling counters is a performance improvement, but means that the
+omitted counters are not returned by &service_info;.</p>
+</note>
+
+</item>
+
+<tag><c>{use_shared_peers, boolean() | [node()] | eval()}</c></tag>
<item>
<p>
Nodes from which communicated peers are made available in
@@ -992,7 +1016,7 @@ the remote candidates list of &app_pick_peer; callbacks.</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
+If <c>eval()</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>.
@@ -1017,6 +1041,15 @@ each node from which requests are sent.</p>
</warning>
</item>
+<tag><c>&transport_opt;</c></tag>
+<item>
+<p>
+Any transport option except <c>applications</c> or
+<c>capabilities</c>.
+Used as defaults for transport configuration, values passed to
+&add_transport; overriding values configured on the service.</p>
+</item>
+
</taglist>
<marker id="transport_opt"/>
@@ -1050,6 +1083,37 @@ implies having to set matching *-Application-Id AVPs in a
</item>
<tag>
+<marker id="avp_dictionaries"/><c>{avp_dictionaries, [module()]}</c></tag>
+<item>
+<p>
+A list of alternate dictionary modules with which to encode/decode
+AVPs that are not defined by the dictionary of the application in
+question.
+At decode, such AVPs are represented as diameter_avp records in the
+<c>'AVP'</c> field of a decoded message or Grouped AVP, the first
+alternate that succeeds in decoding the AVP setting the record's value
+field.
+At encode, values in an <c>'AVP'</c> list can be passed as AVP
+name/value 2-tuples, and it is an encode error for no alternate to
+define the AVP of such a tuple.</p>
+
+<p>
+Defaults to the empty list.</p>
+
+<note>
+<p>
+The motivation for alternate dictionaries is RFC 7683, Diameter
+Overload Indication Conveyance (DOIC), which defines AVPs to
+be piggybacked onto existing application messages rather than defining
+an application of its own.
+The DOIC dictionary is provided by the diameter application, as module
+<c>diameter_gen_doic_rfc7683</c>, but alternate dictionaries can be
+used to encode/decode any set of AVPs not known to an application
+dictionary.</p>
+</note>
+</item>
+
+<tag>
<marker id="capabilities"/><c>{capabilities, [&capability;]}</c></tag>
<item>
<p>
@@ -1064,7 +1128,7 @@ TLS is desired over TCP as implemented by &man_tcp;.</p>
</item>
<tag>
-<marker id="capabilities_cb"/><c>{capabilities_cb, &evaluable;}</c></tag>
+<marker id="capabilities_cb"/><c>{capabilities_cb, &eval;}</c></tag>
<item>
<p>
Callback invoked upon reception of CER/CEA during capabilities
@@ -1158,7 +1222,7 @@ transport.</p>
</item>
<tag>
-<marker id="disconnect_cb"/><c>{disconnect_cb, &evaluable;}</c></tag>
+<marker id="disconnect_cb"/><c>{disconnect_cb, &eval;}</c></tag>
<item>
<p>
Callback invoked prior to terminating the transport process of a
@@ -1258,6 +1322,19 @@ Defaults to 5000.</p>
</item>
<tag>
+<marker id="incoming_maxlen"/><c>{incoming_maxlen, 0..16777215}</c></tag>
+<item>
+<p>
+Bound on the expected size of incoming Diameter messages.
+Messages larger than the specified number of bytes are discarded.</p>
+
+<p>
+Defaults to <c>16777215</c>, the maximum value of the 24-bit Message
+Length field in a Diameter Header.</p>
+
+</item>
+
+<tag>
<marker id="length_errors"/><c>{length_errors, exit|handle|discard}</c></tag>
<item>
<p>
@@ -1315,7 +1392,64 @@ incoming Diameter request.
Options <c>monitor</c> and <c>link</c> are ignored.</p>
<p>
-Defaults to the list configured on the service if not specified.</p>
+Defaults to the empty list.</p>
+</item>
+
+<tag>
+<marker id="strict_capx"/><c>{strict_capx, boolean()]}</c></tag>
+<item>
+<p>
+Whether or not to enforce the RFC 6733 requirement that any message
+before capabilities exchange should close the peer connection.
+If false then unexpected messages are discarded.</p>
+
+<p>
+Defaults to true.
+Changing this results in non-standard behaviour, but can be useful in
+case peers are known to be behave badly.</p>
+</item>
+
+<tag>
+<marker id="strict_mbit"/><c>{strict_mbit, boolean()}</c></tag>
+<item>
+<p>
+Whether or not to regard an AVP setting the M-bit as erroneous when
+the command grammar in question does not explicitly allow the AVP.
+If <c>true</c> then such AVPs are regarded as 5001 errors,
+DIAMETER_AVP_UNSUPPORTED.
+If <c>false</c> then the M-bit is ignored and policing
+it becomes the receiver's responsibility.</p>
+
+<p>
+Defaults to <c>true</c>.</p>
+
+<warning>
+<p>
+RFC 6733 is unclear about the semantics of the M-bit.
+One the one hand, the CCF specification in section 3.2 documents AVP
+in a command grammar as meaning <em>any</em> arbitrary AVP; on the
+other hand, 1.3.4 states that AVPs setting the M-bit cannot be added
+to an existing command: the modified command must instead be
+placed in a new Diameter application.</p>
+<p>
+The reason for the latter is presumably interoperability:
+allowing arbitrary AVPs setting the M-bit in a command makes its
+interpretation implementation-dependent, since there's no
+guarantee that all implementations will understand the same set of
+arbitrary AVPs in the context of a given command.
+However, interpreting <c>AVP</c> in a command grammar as any
+AVP, regardless of M-bit, renders 1.3.4 meaningless, since the receiver
+can simply ignore any AVP it thinks isn't relevant, regardless of the
+sender's intent.</p>
+<p>
+Beware of confusing mandatory in the sense of the M-bit with mandatory
+in the sense of the command grammar.
+The former is a semantic requirement: that the receiver understand the
+semantics of the AVP in the context in question.
+The latter is a syntactic requirement: whether or not the AVP must
+occur in the message in question.</p>
+</warning>
+
</item>
<tag>
@@ -1731,8 +1865,8 @@ An example return value with for a client service with Origin-Host
{raddr,{127,0,0,1}},
{rport,3868},
{reuseaddr,true}]}]},
- {watchdog,{&lt;0.66.0>,{1346,171491,996448},okay}},
- {peer,{&lt;0.67.0>,{1346,171491,999906}}},
+ {watchdog,{&lt;0.66.0>,-576460736368485571,okay}},
+ {peer,{&lt;0.67.0>,-576460736357885808}},
{apps,[{0,common}]},
{caps,[{origin_host,{"client.example.com","server.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1812,8 +1946,8 @@ connection might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {accept,[[{watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {accept,[[{watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -1842,7 +1976,7 @@ connection might look as follows.</p>
{send_max,148},
{send_avg,87},
{send_pend,0}]}]}],
- [{watchdog,{&lt;0.72.0>,{1346,171491,998404},initial}}]]},
+ [{watchdog,{&lt;0.72.0>,-576460638229717546,initial}}]]},
{statistics,[{{{0,280,0},recv},7},
{{{0,280,1},send},7},
{{{0,280,0},recv,{'Result-Code',2001}},7},
@@ -1890,8 +2024,8 @@ A return value for the server above might look as follows.</p>
{transport_config,[{reuseaddr,true},
{ip,{127,0,0,1}},
{port,3868}]}]},
- {watchdog,{&lt;0.56.0>,{1346,171481,226895},okay}},
- {peer,{&lt;0.58.0>,{1346,171491,999511}}},
+ {watchdog,{&lt;0.56.0>,-576460739249514012,okay}},
+ {peer,{&lt;0.58.0>,-576460638229179167}},
{apps,[{0,common}]},
{caps,[{origin_host,{"server.example.com","client.example.com"}},
{origin_realm,{"example.com","example.com"}},
@@ -2087,7 +2221,7 @@ Stop a diameter service.</p>
<p>
Stopping a service causes all associated transport connections to be
broken.
-A DPR message with be sent as in the case of &remove_transport;.</p>
+A DPR message will be sent as in the case of &remove_transport;.</p>
<note>
<p>
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index dfcd00975b..aa334beb21 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -13,7 +13,8 @@
<header>
<copyright>
-<year>2011</year><year>2016</year>
+<year>2011</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -319,7 +320,7 @@ or &peer_down; callback.</p>
<v>Action = Send | Discard | {eval_packet, Action, PostF}</v>
<v>Send = {send, &packet; | &message;}</v>
<v>Discard = {discard, Reason} | discard</v>
-<v>PostF = &mod_evaluable;}</v>
+<v>PostF = &mod_eval;}</v>
</type>
<desc>
<p>
@@ -371,7 +372,7 @@ discarded}</c>.</p>
<v>Action = Send | Discard | {eval_packet, Action, PostF}</v>
<v>Send = {send, &packet; | &message;}</v>
<v>Discard = {discard, Reason} | discard</v>
-<v>PostF = &mod_evaluable;}</v>
+<v>PostF = &mod_eval;}</v>
</type>
<desc>
<p>
@@ -478,7 +479,7 @@ not selected.</p>
| {answer_message, 3000..3999|5000..5999}
| {protocol_error, 3000..3999}</v>
<v>Opt = &mod_call_opt;</v>
-<v>PostF = &mod_evaluable;</v>
+<v>PostF = &mod_eval;</v>
</type>
<desc>
<p>
diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml
index 91e96058dd..0a34dd7ec7 100644
--- a/lib/diameter/doc/src/diameter_codec.xml
+++ b/lib/diameter/doc/src/diameter_codec.xml
@@ -4,7 +4,10 @@
'<seealso marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>'>
<!ENTITY types
'<seealso marker="diameter_dict#DATA_TYPES">diameter_dict(4)</seealso>'>
- <!ENTITY % also SYSTEM "seealso.ent" >
+ <!ENTITY decode_format
+ '<seealso marker="diameter#decode_format">decode format</seealso>'>
+
+<!ENTITY % also SYSTEM "seealso.ent" >
<!ENTITY % here SYSTEM "seehere.ent" >
%also;
%here;
@@ -13,7 +16,8 @@
<erlref>
<header>
<copyright>
-<year>2012</year><year>2016</year>
+<year>2012</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -53,17 +57,17 @@ communicated to &man_app; callbacks.
Similarly, outgoing Diameter messages are encoded into binary() before
being passed to the appropriate &man_transport; module for
transmission.
-The functions in this module implement this encode/decode.</p>
+The functions documented here implement the default encode/decode.</p>
-<note>
+<warning>
<p>
-Calls to this module are made by diameter itself as a consequence of
-configuration passed to &mod_start_service;.
-The encode/decode functions may also be useful for other purposes (eg.
-test) but the diameter user does not need to call them explicitly when
+The diameter user does not need to call functions here explicitly when
sending and receiving messages using &mod_call; and the callback
-interface documented in &man_app;.</p>
-</note>
+interface documented in &man_app;: diameter itself provides encode/decode
+as a consequence of configuration passed to &mod_start_service;, and
+the results may differ from those returned by the functions documented
+here, depending on configuration.</p>
+</warning>
<p>
The &header; and &packet; records below
@@ -144,7 +148,8 @@ question.</p>
<p>
The decoded value of an AVP.
Will be <c>undefined</c> on decode if the data bytes could
-not be decoded or the AVP is unknown.
+not be decoded, the AVP is unknown, or if the &decode_format; is
+<c>none</c>.
The type of a decoded value is as document in &types;.</p>
</item>
@@ -229,7 +234,8 @@ header.</p>
</item>
<tag>
-<marker id="message"/><c>message() = record() | list()</c></tag>
+<marker id="message"/><c>message() = record()
+ | maybe_improper_list()</c></tag>
<item>
<p>
The representation of a Diameter message as passed to
@@ -239,14 +245,17 @@ a message as defined in a dictionary file is encoded as a record with
one field for each component AVP.
Equivalently, a message can also be encoded as a list whose head is
the atom-valued message name (as specified in the relevant dictionary
-file) and whose tail is a list of <c>{AvpName, AvpValue}</c> pairs.</p>
+file) and whose tail is either a list of AVP name/values
+pairs or a map with values keyed on AVP names.
+The format at decode is determined by &mod_decode_format;.
+Any of the formats is accepted at encode.</p>
<p>
Another list-valued representation allows a message to be specified
as a list whose head is a &header; and whose tail is an &avp; list.
This representation is used by diameter itself when relaying requests
as directed by the return value of a &app_handle_request; callback.
-It differs from the other other two in that it bypasses the checks for
+It differs from the other two in that it bypasses the checks for
messages that do not agree with their definitions in the dictionary in
question: messages are sent exactly as specified.</p>
@@ -282,15 +291,16 @@ value other than <c>undefined</c>.</p>
<item>
<p>
The incoming/outgoing message.
-For an incoming message, a record if the message can be
-decoded in a non-relay application, <c>undefined</c> otherwise.
+For an incoming message, a term corresponding to the configured
+&decode_format; if the message can be decoded in a non-relay
+application, <c>undefined</c> otherwise.
For an outgoing message, setting a <c>[&header; | &avp;]</c> list is
equivalent to setting the <c>header</c> and <c>avps</c> fields to the
corresponding values.</p>
<warning>
<p>
-A record-valued <c>msg</c> field does <em>not</em> imply an absence of
+A value in the <c>msg</c> field does <em>not</em> imply an absence of
decode errors.
The <c>errors</c> field should also be examined.</p>
</warning>
diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml
index 9584d682c2..37d30b709b 100644
--- a/lib/diameter/doc/src/diameter_dict.xml
+++ b/lib/diameter/doc/src/diameter_dict.xml
@@ -16,7 +16,8 @@
<header>
<copyright>
-<year>2011</year><year>2016</year>
+<year>2011</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -307,11 +308,11 @@ The P flag has been deprecated by &the_rfc;.</p>
<p>
Specifies AVPs for which module Mod provides encode/decode functions.
The section contents consists of AVP names.
-For each such name, <c>Mod:Name(encode|decode, Type, Data)</c> is
+For each such name, <c>Mod:Name(encode|decode, Type, Data, Opts)</c> is
expected to provide encode/decode for values of the AVP, where Name is
the name of the AVP, Type is it's type as declared in the
-<c>@avp_types</c> section of the dictionary and Data is the value to
-encode/decode.</p>
+<c>@avp_types</c> section of the dictionary, Data is the value to
+encode/decode, and Opts is a term that is passed through encode/decode.</p>
<p>
Example:</p>
@@ -328,8 +329,8 @@ Framed-IP-Address
<item>
<p>
Like <c>@custom_types</c> but requires the specified module to export
-<c>Mod:Type(encode|decode, Name, Data)</c> rather than
-<c>Mod:Name(encode|decode, Type, Data)</c>.</p>
+<c>Mod:Type(encode|decode, Name, Data, Opts)</c> rather than
+<c>Mod:Name(encode|decode, Type, Data, Opts)</c>.</p>
<p>
Example:</p>
@@ -485,9 +486,9 @@ will result in the following record definition given an empty
prefix.</p>
<pre>
--record('SIP-Deregistration-Reason' {'SIP-Reason-Code',
- 'SIP-Reason-Info',
- 'AVP'}).
+-record('SIP-Deregistration-Reason', {'SIP-Reason-Code',
+ 'SIP-Reason-Info',
+ 'AVP'}).
</pre>
<p>
diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml
index 9b6d629f79..62e958870e 100644
--- a/lib/diameter/doc/src/diameter_sctp.xml
+++ b/lib/diameter/doc/src/diameter_sctp.xml
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd" [
+ <!ENTITY man_tcp_sender
+ '<seealso marker="diameter_tcp#sender">diameter_tcp(3)</seealso>'>
<!ENTITY gen_sctp '<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>'>
<!ENTITY gen_sctp_open1
'<seealso marker="kernel:gen_sctp#open-1">gen_sctp:open/1</seealso>'>
@@ -16,7 +18,7 @@
<header>
<copyright>
<year>2011</year>
-<year>2016</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -78,7 +80,11 @@ and implements the behaviour documented in
<v>Reason = term()</v>
<v>OwnOpt = {raddr, &ip_address;}
| {rport, integer()}
- | {accept, Match}</v>
+ | {accept, Match}
+ | {unordered, boolean() | pos_integer()}
+ | {packet, boolean() | raw}
+ | {message_cb, &mod_eval;}
+ | {sender, boolean()}</v>
<v>SctpOpt = term()</v>
<v>Match = &ip_address; | string() | [Match]</v>
</type>
@@ -106,6 +112,41 @@ A string-valued <c>Match</c> that does not parse as an address is
interpreted as a regular expression.</p>
<p>
+Option <c>unordered</c> specifies whether or not to use unordered
+delivery, integer <c>N</c> being equivalent to <c>N =&lt; OS</c>,
+where <c>OS</c> is the number of outbound streams negotiated on the
+association in question.
+Regardless of configuration, sending is ordered on stream 0
+until reception of a second incoming message, to ensure that a peer
+receives capabilities exchange messages before any other.
+Defaults to <c>false</c>.</p>
+
+<p>
+Option <c>packet</c> determines how/if an incoming message is
+packaged into a diameter_packet record.
+If <c>false</c> then messages are received as binary().
+If <c>true</c> then as a record with the binary() message in the
+<c>bin</c> field and a <c>{stream, Id}</c> tuple in the
+<c>transport_data</c> field, where <c>Id</c> is the identifier of the
+inbound stream the message was received on.
+If <c>raw</c> then as a record with the received ancillary
+sctp_sndrcvinfo record in the <c>transport_data</c> field.
+Defaults to <c>true</c>.</p>
+
+<p>
+Options <c>message_cb</c> and <c>sender</c> have semantics identical
+to those documented in &man_tcp_sender;, but with the message argument
+to a <c>recv</c> callback being as directed by the <c>packet</c>
+option.</p>
+
+<p>
+An <c>{outstream, Id}</c> tuple in the <c>transport_data</c> field of
+a outgoing diameter_packet record sets the outbound stream on which
+the message is sent, modulo the negotiated number of outbound streams.
+Any other value causes successive such sends to cycle though all
+outbound streams.</p>
+
+<p>
Remaining options are any accepted by &gen_sctp_open1;, with the exception
of options <c>mode</c>, <c>binary</c>, <c>list</c>, <c>active</c>
and <c>sctp_events</c>.
@@ -116,35 +157,21 @@ and port respectively.</p>
Multiple <c>ip</c> options can be specified for a multihomed peer.
If none are specified then the values of <c>Host-IP-Address</c>
in the <c>diameter_service</c> record are used.
-(In particular, one of these must be specified.)
Option <c>port</c> defaults to 3868 for a listening transport and 0 for a
connecting transport.</p>
<warning>
<p>
-An insufficiently large receive buffer may result in a peer having to
+An small receive buffer may result in a peer having to
resend incoming messages: set the &inet; option <c>recbuf</c> to increase
the buffer size.</p>
<p>
-An insufficiently large send buffer may result in outgoing messages
+An small send buffer may result in outgoing messages
being discarded: set the &inet; option <c>sndbuf</c> to increase
the buffer size.</p>
</warning>
-<p>
-The <c>transport_data</c> field of record <c>diameter_packet</c>
-is used to communicate the stream on which an inbound message
-has been received, or on which an outbound message should be sent.
-The value will be of the form <c>{stream, Id}</c> for an inbound
-message passed to a &app_handle_request; or &app_handle_answer;
-callback.
-For an outbound message, <c>{outstream, Id}</c> in the return value of
-&app_handle_request; or &app_prepare_retransmit; sets the outbound
-stream, the stream id being interpreted modulo the number of outbound
-streams.
-Any other value, or not setting a value, causes successive such sends
-to cycle though all outbound streams.</p>
</desc>
</func>
diff --git a/lib/diameter/doc/src/diameter_soc.xml b/lib/diameter/doc/src/diameter_soc.xml
index ae404fcda4..2d2d66a243 100644
--- a/lib/diameter/doc/src/diameter_soc.xml
+++ b/lib/diameter/doc/src/diameter_soc.xml
@@ -1,15 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd" [
+ <!ENTITY gen_sctp '<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>'>
+ <!ENTITY gen_tcp '<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>'>
+ <!ENTITY service '<seealso marker="diameter#start_service-2">service</seealso>'>
+ <!ENTITY capabilities '<seealso marker="diameter#capability">capabilities</seealso>'>
+ <!ENTITY events '<seealso marker="diameter#service_event">events</seealso>'>
+ <!ENTITY NA '&#8212;'>
+ <!ENTITY BR '<br/>&nbsp;<br/>'>
<!ENTITY % also SYSTEM "seealso.ent" >
%also;
]>
-<chapter xmlns:xi="http://www.w3.org/2001/XInclude">
+<chapter>
<header>
<copyright>
<year>2011</year>
-<year>2016</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
@@ -41,63 +48,1285 @@ limitations under the License.
</header>
<p>
-Known points of questionable or non-compliance.</p>
+The table below summarizes the diameter application's compliance with
+&the_rfc;.
+Since the diameter application isn't a Diameter node on its own,
+compliance is strictly the responsibility of the user in many cases,
+diameter providing the means for the user to be compliant
+rather than being compliant on its own.</p>
-<!-- ===================================================================== -->
-
-<section>
-<title>&the_rfc;</title>
-
-<list>
-
-<item>
-<p>
-There is no support for DTLS over SCTP.</p>
-</item>
-
-<item>
<p>
-There is no explicit support for peer discovery (section 5.2).
-It can possibly be implemented on top of diameter as is but this is
-probably something that diameter should do.</p>
-</item>
+The Compliance column notes <em>C</em> (Compliant) if the required
+functionality is implemented, <em>PC</em> (Partially Compliant) if
+there are limitations, <em>NC</em> (Not Compliant) if functionality is
+not implemented, or a dash if text is informational or only places
+requirements that must be met by the user's implementation.</p>
-<item>
<p>
-The peer state machine's election process (section 5.6.4) isn't
-implemented as specified since it assumes knowledge of a
-peer's Origin-Host before sending it a CER. (The identity becoming known
-upon reception of CEA.)
-The possibility of configuring
-the peer's Origin-Host could be added, along with handling of the case
-that it sends something else, but for many applications this will
-just be unnecessary configuration of a value that it has no control over.</p>
-</item>
-<!-- Transport protocol plus address/port, which we do know when
- sending and receiving CER, is enough to definitely identify
- the peer. However, there's nothing stopping a peer from using
- different identities on different transport protocols, even
- if it's maybe a bit far-fetched. -->
-
-</list>
-
-<xi:include href="diameter_soc_rfc6733.xml"/>
-
-</section>
+Capitalized <em>Diameter</em> refers to the protocol, lowercase
+<em>diameter</em> to the Erlang application.</p>
<!-- ===================================================================== -->
<section>
-<title>RFC 3539</title>
+<title>&the_rfc; - Diameter Base Protocol</title>
-<p>
-RFC 3539 is more difficult to comply to since it discusses
-problems as much as it requires functionality but all the MUST's are
-covered, the watchdog state machine being the primary one.
-Of the optional functionality, load balancing is left to the
-diameter user (since it's the one deciding who to send to) and
-there is no Congestion Manager.</p>
+<table>
+<row>
+ <cell><em>Section</em></cell>
+ <cell><em>Title</em></cell>
+ <cell><em>Compliance</em></cell>
+ <cell><em>Notes</em></cell>
+</row>
+<row>
+ <cell>1</cell>
+ <cell>Introduction</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>1.1</cell>
+ <cell>Diameter Protocol</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>1.1.1</cell>
+ <cell>Description of the Document Set</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>1.1.2</cell>
+ <cell>Conventions Used in This Document</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>1.1.3</cell>
+ <cell>Changes from RFC 3588</cell>
+ <cell>&NA;</cell>
+ <cell>It is possible to configure a 3588 dictionary in
+ order to get 3588 semantics, where the differ from 6733.</cell>
+</row>
+<row>
+ <cell>1.2</cell>
+ <cell>Terminology</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>1.3</cell>
+ <cell>Approach to Extensibility</cell>
+ <cell>&NA;</cell>
+ <cell>The dictionary interface documented in &man_dict; provides
+ extensibility, allowing the user to defined new AVPs, commands, and
+ applications.
+ Ready dictionaries are provided for the &the_rfc; common message, base
+ accounting, and relay applications, as well as for RFC 7683,
+ Diameter Overload Indicator Conveyance.</cell>
+</row>
+<row>
+ <cell>1.3.1</cell>
+ <cell>Defining New AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>1.3.2</cell>
+ <cell>Creating New AVPs</cell>
+ <cell>&NA;</cell>
+ <cell>New AVPs can be defined using the dictionary interface.
+ Both RFC data formats and extensions are supported.</cell>
+</row>
+<row>
+ <cell>1.3.3</cell>
+ <cell>Creating New Commands</cell>
+ <cell>&NA;</cell>
+ <cell>New commands can be defined using the dictionary interface.</cell>
+</row>
+<row>
+ <cell>1.3.4</cell>
+ <cell>Creating New Diameter Applications</cell>
+ <cell>&NA;</cell>
+ <cell>New applications can be defined using the dictionary interface.</cell>
+</row>
+<row>
+ <cell>2</cell>
+ <cell>Protocol Overview</cell>
+ <cell>&NA;</cell>
+ <cell>Session state is the responsibility of the user.&BR;
+ The role of a Diameter node is determined by the user's
+ implementation.</cell>
+</row>
+<row>
+ <cell>2.1</cell>
+ <cell>Transport</cell>
+ <cell>PC</cell>
+ <cell>Ports are configured by the user: diameter places no
+ restrictions.&BR;
+ The transport interface documented in &man_transport;
+ allows the user to implement their own methods.
+ Ready support is provided for TCP, TCP/TLS, and SCTP, but not
+ DTLS/SCTP.&BR;
+ Multiple connections to the same peer is possible.
+ ICMP messages are not interpreted.</cell>
+</row>
+<row>
+ <cell>2.1.1</cell>
+ <cell>SCTP Guidelines</cell>
+ <cell>C</cell>
+ <cell>Unordered sending is configurable in &man_sctp;.
+ There is no special handling of DPR/DPA: since a user that cares
+ about pending answers should wait for them before initiating
+ DPR.&BR;
+ A PPID can be configured with a a gen_sctp sctp_default_send_param
+ option.</cell>
+</row>
+<row>
+ <cell>2.2</cell>
+ <cell>Securing Diameter Messages</cell>
+ <cell>PC</cell>
+ <cell>DTLS is not supported by &man_sctp;. See also
+ 2.1.</cell>
+</row>
+<row>
+ <cell>2.3</cell>
+ <cell>Diameter Application Compliance</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>2.4</cell>
+ <cell>Application Identifiers</cell>
+ <cell>C</cell>
+ <cell>The user configures diameter with the identifiers to send at
+ capabilities exchange, along with corresponding dictionaries
+ defining the messages of the applications.</cell>
+</row>
+<row>
+ <cell>2.5</cell>
+ <cell>Connections vs. Sessions</cell>
+ <cell>C</cell>
+ <cell>Connections are realized by configuring transport. Sessions
+ are the responsibility of the user.</cell>
+</row>
+<row>
+ <cell>2.6</cell>
+ <cell>Peer Table</cell>
+ <cell>PC</cell>
+ <cell>Routing is implemented by the user in callbacks documented in
+ &man_app;.
+ A peer table of the documented form is not exposed to the user.</cell>
+</row>
+<row>
+ <cell>2.7</cell>
+ <cell>Routing Table</cell>
+ <cell>PC</cell>
+ <cell>See 2.6.
+ A routing table of the documented form is not exposed to
+ the user.</cell>
+</row>
+<row>
+ <cell>2.8</cell>
+ <cell>Role of Diameter Agents</cell>
+ <cell>C</cell>
+ <cell>Most role-specific behaviour is implemented by the user.
+ How a node advertises itself at capabilities exchange is determined
+ by user configuration.</cell>
+</row>
+<row>
+ <cell>2.8.1</cell>
+ <cell>Relay Agents</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>2.8.2</cell>
+ <cell>Proxy Agents</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>2.8.3</cell>
+ <cell>Redirect Agents</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>2.8.4</cell>
+ <cell>Translation Agents</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>2.9</cell>
+ <cell>Diameter Path Authorization</cell>
+ <cell>&NA;</cell>
+ <cell>Authorization is the responsibility of the user.</cell>
+</row>
+<row>
+ <cell>3</cell>
+ <cell>Diameter Header</cell>
+ <cell>C</cell>
+ <cell>Hop-by-Hop and End-to-End Identifiers are set by diameter when
+ sending outgoing requests.</cell>
+</row>
+<row>
+ <cell>3.1</cell>
+ <cell>Command Codes</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>3.2</cell>
+ <cell>Command Code Format Specification</cell>
+ <cell>C</cell>
+ <cell>Commands are defined as CCF specifications in dictionary
+ files.</cell>
+</row>
+<row>
+ <cell>3.3</cell>
+ <cell>Diameter Command Naming Conventions</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>4</cell>
+ <cell>Diameter AVPs</cell>
+ <cell>C</cell>
+ <cell>Any required padding is added by diameter when encoding
+ outgoing messages.</cell>
+</row>
+<row>
+ <cell>4.1</cell>
+ <cell>AVP Header</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>4.1.1</cell>
+ <cell>Optional Header Elements</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>4.2</cell>
+ <cell>Basic AVP Data Formats</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>4.3</cell>
+ <cell>Derived AVP Data Formats</cell>
+ <cell>C</cell>
+ <cell>Arbitrary derived data formats are supported by the dictionary
+ interface.</cell>
+</row>
+<row>
+ <cell>4.3.1</cell>
+ <cell>Common Derived AVP Data Formats</cell>
+ <cell>C</cell>
+ <cell>Beware that RFC 6733 changed the DiameterURI transport/port
+ defaults specified in RFC3588.
+ Relying on the defaults can result in interoperability
+ problems.</cell>
+</row>
+<row>
+ <cell>4.4</cell>
+ <cell>Grouped AVP Values</cell>
+ <cell>C</cell>
+ <cell>The M-bit on a component AVP of a Grouped AVP that does not
+ set M is ignored: such AVPs are not regarded as erroneous at
+ decode.&BR;
+ Grouped AVPs are defined as CCF specifications in dictionary
+ files.</cell>
+</row>
+<row>
+ <cell>4.4.1</cell>
+ <cell>Example AVP with a Grouped Data Type</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>4.5</cell>
+ <cell>Diameter Base Protocol AVPs</cell>
+ <cell>C</cell>
+ <cell>The base AVPs are defined in the common dictionary provided by
+ diameter.
+ There are common dictionaries for both RFC 3588 and RFC 6733 since
+ the latter made changes to both syntax and semantics.</cell>
+</row>
+<row>
+ <cell>5</cell>
+ <cell>Diameter Peers</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.1</cell>
+ <cell>Peer Connections</cell>
+ <cell>PC</cell>
+ <cell>A peer's DiameterIdentity is not required when initiating a
+ connection: the identify is received at capabilities exchange, at
+ which time the connection can be rejected if the identity is
+ objectionable.&BR;
+ The number of connections established depends on the user's
+ configuration. Multiple connections per peer is possible.</cell>
+</row>
+<row>
+ <cell>5.2</cell>
+ <cell>Diameter Peer Discovery</cell>
+ <cell>NC</cell>
+ <cell>No form of peer discovery is implemented.
+ The user can implement this independently of diameter if
+ required.</cell>
+</row>
+<row>
+ <cell>5.3</cell>
+ <cell>Capabilities Exchange</cell>
+ <cell>C</cell>
+ <cell>All supported applications are sent in CEA.
+ The user can reject an incoming CER or CEA in a configured
+ callback.&BR;
+ Both transport security at connection establishment and
+ negotiated via an Inband-Security AVP are supported.</cell>
+</row>
+<row>
+ <cell>5.3.1</cell>
+ <cell>Capabilities-Exchange-Request</cell>
+ <cell>C</cell>
+ <cell>CER is sent and received by diameter.</cell>
+</row>
+<row>
+ <cell>5.3.2</cell>
+ <cell>Capabilities-Exchange-Answer</cell>
+ <cell>C</cell>
+ <cell>CEA is sent and received by diameter.</cell>
+</row>
+<row>
+ <cell>5.3.3</cell>
+ <cell>Vendor-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.3.4</cell>
+ <cell>Firmware-Revision AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.3.5</cell>
+ <cell>Host-IP-Address AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.3.6</cell>
+ <cell>Supported-Vendor-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.3.7</cell>
+ <cell>Product-Name AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.4</cell>
+ <cell>Disconnecting Peer Connections</cell>
+ <cell>C</cell>
+ <cell>DPA will not be answered with error: a peer that wants to a
+ avoid a race can wait for pending answers before sending
+ DPR.</cell>
+</row>
+<row>
+ <cell>5.4.1</cell>
+ <cell>Disconnect-Peer-Request</cell>
+ <cell>C</cell>
+ <cell>DPR is sent by diameter in response to configuration
+ changes requiring a connection to be broken.
+ The user can also send DPR.</cell>
+</row>
+<row>
+ <cell>5.4.2</cell>
+ <cell>Disconnect-Peer-Answer</cell>
+ <cell>C</cell>
+ <cell>DPR is answered by diameter.</cell>
+</row>
+<row>
+ <cell>5.4.3</cell>
+ <cell>Disconnect-Cause AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.5</cell>
+ <cell>Transport Failure Detection</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.5.1</cell>
+ <cell>Device-Watchdog-Request</cell>
+ <cell>C</cell>
+ <cell>DWR is sent and received by diameter.
+ Callbacks notify the user of transitions into and out of the OKAY
+ state.</cell>
+</row>
+<row>
+ <cell>5.5.2</cell>
+ <cell>Device-Watchdog-Answer</cell>
+ <cell>C</cell>
+ <cell>DWA is sent and received by diameter.</cell>
+</row>
+<row>
+ <cell>5.5.3</cell>
+ <cell>Transport Failure Algorithm</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.5.4</cell>
+ <cell>Failover and Failback Procedures</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.6</cell>
+ <cell>Peer State Machine</cell>
+ <cell>PC</cell>
+ <cell>The election process is modified as described in 5.6.4.</cell>
+</row>
+<row>
+ <cell>5.6.1</cell>
+ <cell>Incoming Connections</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.6.2</cell>
+ <cell>Events</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.6.3</cell>
+ <cell>Actions</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>5.6.4</cell>
+ <cell>The Election Process</cell>
+ <cell>PC</cell>
+ <cell>As documented, the election assumes knowledge of a peer's
+ DiameterIdentity when initiating a connection, which diameter
+ doesn't require. Connections will be accepted if configuration
+ allows multiple connections per peer to be established or there is
+ no existing connection. Note that the election process is only
+ applicable when multiple connections per peer is
+ disallowed.</cell>
+</row>
+<row>
+ <cell>6</cell>
+ <cell>Diameter Message Processing</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.1</cell>
+ <cell>Diameter Request Routing Overview</cell>
+ <cell>&NA;</cell>
+ <cell>Routing is performed by the user.
+ A callback from diameter provides a list of available suitable peer
+ connections.</cell>
+</row>
+<row>
+ <cell>6.1.1</cell>
+ <cell>Originating a Request</cell>
+ <cell>C</cell>
+ <cell>Requests are constructed by the user; diameter sets header
+ fields as defined in the relevant dictionary.</cell>
+</row>
+<row>
+ <cell>6.1.2</cell>
+ <cell>Sending a Request</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.1.3</cell>
+ <cell>Receiving Requests</cell>
+ <cell>C</cell>
+ <cell>Loops are detected by diameter when the return value of a
+ request callback asks that a request be forwarded.
+ Loop detection in other cases is the responsibility of the
+ user.</cell>
+</row>
+<row>
+ <cell>6.1.4</cell>
+ <cell>Processing Local Requests</cell>
+ <cell>C</cell>
+ <cell>The user decides whether or not to process a request locally
+ in the request callback from diameter.</cell>
+</row>
+<row>
+ <cell>6.1.5</cell>
+ <cell>Request Forwarding</cell>
+ <cell>PC</cell>
+ <cell>See 2.6.</cell>
+</row>
+<row>
+ <cell>6.1.6</cell>
+ <cell>Request Routing</cell>
+ <cell>PC</cell>
+ <cell>See 2.7.</cell>
+</row>
+<row>
+ <cell>6.1.7</cell>
+ <cell>Predictive Loop Avoidance</cell>
+ <cell>C</cell>
+ <cell>See 6.1.3.</cell>
+</row>
+<row>
+ <cell>6.1.8</cell>
+ <cell>Redirecting Requests</cell>
+ <cell>PC</cell>
+ <cell>See 2.6.</cell>
+</row>
+<row>
+ <cell>6.1.9</cell>
+ <cell>Relaying and Proxying Requests</cell>
+ <cell>C</cell>
+ <cell>A Route-Record AVP is appended by diameter when the return
+ value of a request callback asks that a request be forwarded.
+ Appending the AVP in other cases is the responsibility of the
+ user.</cell>
+</row>
+<row>
+ <cell>6.2</cell>
+ <cell>Diameter Answer Processing</cell>
+ <cell>C</cell>
+ <cell>Answer message are constructed by the user, except in the case
+ of some protocol errors, in which case the procedures are
+ followed.</cell>
+</row>
+<row>
+ <cell>6.2.1</cell>
+ <cell>Processing Received Answers</cell>
+ <cell>C</cell>
+ <cell>Answers with an unknown Hop-by-Hop Identifier are
+ discarded.</cell>
+</row>
+<row>
+ <cell>6.2.2</cell>
+ <cell>Relaying and Proxying Answers</cell>
+ <cell>&NA;</cell>
+ <cell>Modifying answers is the responsibility of the user in
+ callbacks from diameter.</cell>
+</row>
+<row>
+ <cell>6.3</cell>
+ <cell>Origin-Host AVP</cell>
+ <cell>C</cell>
+ <cell>The order of AVPs in an encoded message is determined by
+ the CCF of the message in question.&BR;
+ AVPs defined in the RFC are defined in dictionaries provided by
+ diameter.
+ Their proper use in application messages is the responsibility of
+ the user.</cell>
+</row>
+<row>
+ <cell>6.4</cell>
+ <cell>Origin-Realm AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.5</cell>
+ <cell>Destination-Host AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.6</cell>
+ <cell>Destination-Realm AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.7</cell>
+ <cell>Routing AVPs</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.7.1</cell>
+ <cell>Route-Record AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.7.2</cell>
+ <cell>Proxy-Info AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.7.3</cell>
+ <cell>Proxy-Host AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.7.4</cell>
+ <cell>Proxy-State AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.8</cell>
+ <cell>Auth-Application-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.9</cell>
+ <cell>Acct-Application-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.10</cell>
+ <cell>Inband-Security-Id AVP</cell>
+ <cell>C</cell>
+ <cell>See 2.1.</cell>
+</row>
+<row>
+ <cell>6.11</cell>
+ <cell>Vendor-Specific-Application-Id AVP</cell>
+ <cell>C</cell>
+ <cell>Note that the CCF of this AVP is not the same as in RFC
+ 3588.</cell>
+</row>
+<row>
+ <cell>6.12</cell>
+ <cell>Redirect-Host AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.13</cell>
+ <cell>Redirect-Host-Usage AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>6.14</cell>
+ <cell>Redirect-Max-Cache-Time AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>7</cell>
+ <cell>Error Handling</cell>
+ <cell>C</cell>
+ <cell>Answers are formulated by the user in most cases.
+ Answers setting the E-bit can be sent by diameter itself in response
+ to a request that cannot be handled by the user.</cell>
+</row>
+<row>
+ <cell>7.1</cell>
+ <cell>Result-Code AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>7.1.1</cell>
+ <cell>Informational</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>7.1.2</cell>
+ <cell>Success</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>7.1.3</cell>
+ <cell>Protocol Errors</cell>
+ <cell>C</cell>
+ <cell>Result codes 3001, 3002, 3005, and 3007 can be sent in answers
+ formulated by diameter, if configured to do so.</cell>
+</row>
+<row>
+ <cell>7.1.4</cell>
+ <cell>Transient Failures</cell>
+ <cell>C</cell>
+ <cell>Result code 4003 is sent in CEA if there is an existing
+ connection to the peer in question and configuration does not allow
+ more than one.</cell>
+</row>
+<row>
+ <cell>7.1.5</cell>
+ <cell>Permanent Failures</cell>
+ <cell>C</cell>
+ <cell>Message reception detects 5001, 5004,
+ 5005, 5008, 5009, 5010, 5011, 5014, 5015, and 5017 errors.
+ It ignores 5013 errors at the admonition of sections 3 and 4.1.&BR;
+ Note that RFC 3588 did not allow 5xxx result codes in
+ answers setting the E-bit, while RFC 6733 does.
+ This is a potential interoperability problem since the Diameter
+ protocol version has not changed.</cell>
+</row>
+<row>
+ <cell>7.2</cell>
+ <cell>Error Bit</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>7.3</cell>
+ <cell>Error-Message AVP</cell>
+ <cell>C</cell>
+ <cell>The user can include this AVP as required.</cell>
+</row>
+<row>
+ <cell>7.4</cell>
+ <cell>Error-Reporting-Host AVP</cell>
+ <cell>C</cell>
+ <cell>The user can include this AVP as required.</cell>
+</row>
+<row>
+ <cell>7.5</cell>
+ <cell>Failed-AVP AVP</cell>
+ <cell>C</cell>
+ <cell>The user constructs application-specific messages, but
+ diameter provides failed AVPs in message callbacks. Failed component AVPs
+ are grouped within the relevant Grouped AVPs.</cell>
+</row>
+<row>
+ <cell>7.6</cell>
+ <cell>Experimental-Result AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>7.7</cell>
+ <cell>Experimental-Result-Code AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8</cell>
+ <cell>Diameter User Sessions</cell>
+ <cell>&NA;</cell>
+ <cell>Authorization and accounting AVPs are defined in provided
+ dictionaries. Their proper use is the responsibility of the
+ user.</cell>
+</row>
+<row>
+ <cell>8.1</cell>
+ <cell>Authorization Session State Machine</cell>
+ <cell>&NA;</cell>
+ <cell>Authorization is the responsibility of the user: diameter does
+ not implement this state machine.</cell>
+</row>
+<row>
+ <cell>8.2</cell>
+ <cell>Accounting Session State Machine</cell>
+ <cell>&NA;</cell>
+ <cell>Accounting is the responsibility of the user: diameter does
+ not implement this state machine.</cell>
+</row>
+<row>
+ <cell>8.3</cell>
+ <cell>Server-Initiated Re-Auth</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.3.1</cell>
+ <cell>Re-Auth-Request</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.3.2</cell>
+ <cell>Re-Auth-Answer</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.4</cell>
+ <cell>Session Termination</cell>
+ <cell>&NA;</cell>
+ <cell>Session-related messages and AVPs are defined in provided
+ dictionaries. Their proper use is the user's responsibility.</cell>
+</row>
+<row>
+ <cell>8.4.1</cell>
+ <cell>Session-Termination-Request</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.4.2</cell>
+ <cell>Session-Termination-Answer</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.5</cell>
+ <cell>Aborting a Session</cell>
+ <cell>&NA;</cell>
+ <cell>Session-related messages and AVPs are defined in provided
+ dictionaries. Their proper use is the user's responsibility.</cell>
+</row>
+<row>
+ <cell>8.5.1</cell>
+ <cell>Abort-Session-Request</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.5.2</cell>
+ <cell>Abort-Session-Answer</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.6</cell>
+ <cell>Inferring Session Termination from Origin-State-Id</cell>
+ <cell>&NA;</cell>
+ <cell>Session-related messages and AVPs are defined in provided
+ dictionaries. Their proper use is the user's responsibility.</cell>
+</row>
+<row>
+ <cell>8.7</cell>
+ <cell>Auth-Request-Type AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.8</cell>
+ <cell>Session-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.9</cell>
+ <cell>Authorization-Lifetime AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.10</cell>
+ <cell>Auth-Grace-Period AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.11</cell>
+ <cell>Auth-Session-State AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.12</cell>
+ <cell>Re-Auth-Request-Type AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.13</cell>
+ <cell>Session-Timeout AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.14</cell>
+ <cell>User-Name AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.15</cell>
+ <cell>Termination-Cause AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.16</cell>
+ <cell>Origin-State-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.17</cell>
+ <cell>Session-Binding AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.18</cell>
+ <cell>Session-Server-Failover AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.19</cell>
+ <cell>Multi-Round-Time-Out AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.20</cell>
+ <cell>Class AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>8.21</cell>
+ <cell>Event-Timestamp AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9</cell>
+ <cell>Accounting</cell>
+ <cell>&NA;</cell>
+ <cell>Accounting-related messages and AVPs are defined in provided
+ dictionaries. Their proper use is the user's responsibility.</cell>
+</row>
+<row>
+ <cell>9.1</cell>
+ <cell>Server Directed Model</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.2</cell>
+ <cell>Protocol Messages</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.3</cell>
+ <cell>Accounting Application Extension and Requirements</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.4</cell>
+ <cell>Fault Resilience</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.5</cell>
+ <cell>Accounting Records</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.6</cell>
+ <cell>Correlation of Accounting Records</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.7</cell>
+ <cell>Accounting Command Codes</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.7.1</cell>
+ <cell>Accounting-Request</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.7.2</cell>
+ <cell>Accounting-Answer</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8</cell>
+ <cell>Accounting AVPs</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8.1</cell>
+ <cell>Accounting-Record-Type AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8.2</cell>
+ <cell>Acct-Interim-Interval AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8.3</cell>
+ <cell>Accounting-Record-Number AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8.4</cell>
+ <cell>Acct-Session-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8.5</cell>
+ <cell>Acct-Multi-Session-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8.6</cell>
+ <cell>Accounting-Sub-Session-Id AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>9.8.7</cell>
+ <cell>Accounting-Realtime-Required AVP</cell>
+ <cell>C</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>10</cell>
+ <cell>AVP Occurrence Tables</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>10.1</cell>
+ <cell>Base Protocol Command AVP Table</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>10.2</cell>
+ <cell>Accounting AVP Table</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11</cell>
+ <cell>IANA Considerations</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.1</cell>
+ <cell>AVP Header</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.1.1</cell>
+ <cell>AVP Codes</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.1.2</cell>
+ <cell>AVP Flags</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.2</cell>
+ <cell>Diameter Header</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.2.1</cell>
+ <cell>Command Codes</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.2.2</cell>
+ <cell>Command Flags</cell>
+ <cell></cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3</cell>
+ <cell>AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.1</cell>
+ <cell>Experimental-Result-Code AVP</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.2</cell>
+ <cell>Result-Code AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.3</cell>
+ <cell>Accounting-Record-Type AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.4</cell>
+ <cell>Termination-Cause AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.5</cell>
+ <cell>Redirect-Host-Usage AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.6</cell>
+ <cell>Session-Server-Failover AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.7</cell>
+ <cell>Session-Binding AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.8</cell>
+ <cell>Disconnect-Cause AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.9</cell>
+ <cell>Auth-Request-Type AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.10</cell>
+ <cell>Auth-Session-State AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.11</cell>
+ <cell>Re-Auth-Request-Type AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.12</cell>
+ <cell>Accounting-Realtime-Required AVP Values</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.3.13</cell>
+ <cell>Inband-Security-Id AVP (code 299)</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.4</cell>
+ <cell>_diameters Service Name and Port Number Registration</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.5</cell>
+ <cell>SCTP Payload Protocol Identifiers</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>11.6</cell>
+ <cell>S-NAPTR Parameters</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>12</cell>
+ <cell>Diameter Protocol-Related Configurable Parameters</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>13</cell>
+ <cell>Security Considerations</cell>
+ <cell>PC</cell>
+ <cell>See 2.1.&BR;
+ IPsec is transparent to diameter.</cell>
+</row>
+<row>
+ <cell>13.1</cell>
+ <cell>TLS/TCP and DTLS/SCTP Usage</cell>
+ <cell>PC</cell>
+ <cell>See 2.1.</cell>
+</row>
+<row>
+ <cell>13.2</cell>
+ <cell>Peer-to-Peer Considerations</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>13.3</cell>
+ <cell>AVP Considerations</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>14</cell>
+ <cell>References</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>14.1</cell>
+ <cell>Normative References</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+<row>
+ <cell>14.2</cell>
+ <cell>Informative References</cell>
+ <cell>&NA;</cell>
+ <cell></cell>
+</row>
+
+<tcaption>RFC 6733 Compliance</tcaption>
+</table>
</section>
</chapter>
+
+<!-- LocalWords: AVP AVPs CCF DiameterIdentity CEA CER Inband IP
+-->
+<!-- LocalWords: DPA DPR DWR DWA Failover Failback Proxying Auth
+-->
+<!-- LocalWords: interoperability Multi Timestamp Realtime
+-->
diff --git a/lib/diameter/doc/src/diameter_soc_rfc6733.xml b/lib/diameter/doc/src/diameter_soc_rfc6733.xml
deleted file mode 100644
index 2098965706..0000000000
--- a/lib/diameter/doc/src/diameter_soc_rfc6733.xml
+++ /dev/null
@@ -1,8693 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-
-<!--
-
-<copyright>
-<year>2013</year><year>2016</year>
-<holder>Ericsson AB. All Rights Reserved.</holder>
-</copyright>
-
-<legalnotice>
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-</legalnotice>
-
--->
-
-<!DOCTYPE section SYSTEM "chapter.dtd" [
- <!ENTITY gen_sctp '<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>'>
- <!ENTITY gen_tcp '<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>'>
- <!ENTITY service '<seealso marker="diameter#start_service-2">service</seealso>'>
- <!ENTITY capabilities '<seealso marker="diameter#capability">capabilities</seealso>'>
- <!ENTITY events '<seealso marker="diameter#service_event">events</seealso>'>
- <!ENTITY nada '<p>No comment.</p>'>
- <!ENTITY % also SYSTEM "seealso.ent" >
- %also;
-]>
-
-<section>
-<title>Commentary</title>
-
-<p>
-A more detailed commentary on &the_rfc; follows.
-Its purpose is to (hopefully) clarify not only what is supported but
-how, given that semantics and features discussed in the RFC are not
-solely the responsibility of the diameter application:
-in many cases much depends on the configuration a user passes to
-diameter, the implementation of &man_app; callback modules in
-particular.</p>
-
-<p>
-Comments apply to all text following the preceding comment.
-Be sure to distinguish between capitalized <em>Diameter</em>, the
-protocol defined by the RFC, and lowercase <em>diameter</em>, the
-Erlang application to which the commentary applies.</p>
-
-<warning>
-<p>
-The commentary is not yet complete.
-Comments currently stop at chapter 4.</p>
-</warning>
-
-<pre>
-Fajardo, et al. Standards Track [Page 6]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-1. Introduction
-
- Authentication, Authorization, and Accounting (AAA) protocols such as
- TACACS [RFC1492] and RADIUS [RFC2865] were initially deployed to
- provide dial-up PPP [RFC1661] and terminal server access. Over time,
- AAA support was needed on many new access technologies, the scale and
- complexity of AAA networks grew, and AAA was also used on new
- applications (such as voice over IP). This led to new demands on AAA
- protocols.
-</pre>
-
-<p>
-Note that diameter implements the Diameter protocol as defined in
-&the_rfc;.
-It also supported the previous version of the protocol, as defined in
-RFC 3588, when there are differences.
-(Which will be noted below.)
-It does not support RADIUS.</p>
-
-<pre>
-
- Network access requirements for AAA protocols are summarized in
- Aboba, et al. [RFC2989]. These include:
-
- Failover
-
- [RFC2865] does not define failover mechanisms and, as a result,
- failover behavior differs between implementations. In order to
- provide well-defined failover behavior, Diameter supports
- application-layer acknowledgements and defines failover algorithms
- and the associated state machine.
-</pre>
-
-&nada;
-
-<pre>
-
- Transmission-level security
-
- RADIUS [RFC2865] defines an application-layer authentication and
- integrity scheme that is required only for use with response
- packets. While [RFC2869] defines an additional authentication and
- integrity mechanism, use is only required during Extensible
- Authentication Protocol (EAP) [RFC3748] sessions. While attribute
- hiding is supported, [RFC2865] does not provide support for per-
- packet confidentiality. In accounting, [RFC2866] assumes that
- replay protection is provided by the backend billing server rather
- than within the protocol itself.
-
- While [RFC3162] defines the use of IPsec with RADIUS, support for
- IPsec is not required. In order to provide universal support for
- transmission-level security, and enable both intra- and inter-
- domain AAA deployments, Diameter provides support for TLS/TCP and
- DTLS/SCTP. Security is discussed in Section 13.
-</pre>
-
-<p>
-Whether or not IPsec is used is transparent to diameter.</p>
-
-<p>
-The transport protocol used on a given peer connection is also
-transparent to diameter in that transport to diameter is simply a
-module that implements the transport protocol documented in
-&man_transport;.
-A diameter user configures this module as the &mod_transport_opt;
-<c>transport_module</c>.</p>
-
-<p>
-While a user can implement their own transport modules, diameter
-includes implementations for TCP and SCTP:
-&man_tcp; based on &gen_tcp; and &man_sctp; based on &gen_sctp;.
-The former supports TLS but the latter does not currently support
-DTLS.</p>
-
-<pre>
-
- Reliable transport
-
- RADIUS runs over UDP, and does not define retransmission behavior;
- as a result, reliability varies between implementations. As
- described in [RFC2975], this is a major issue in accounting, where
- packet loss may translate directly into revenue loss. In order to
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 7]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- provide well-defined transport behavior, Diameter runs over
- reliable transport mechanisms (TCP, Stream Control Transmission
- Protocol (SCTP)) as defined in [RFC3539].
-
- Agent support
-
- RADIUS does not provide for explicit support for agents, including
- proxies, redirects, and relays. Since the expected behavior is
- not defined, it varies between implementations. Diameter defines
- agent behavior explicitly; this is described in Section 2.8.
-</pre>
-
-&nada;
-
-<pre>
-
- Server-initiated messages
-
- While server-initiated messages are defined in RADIUS [RFC5176],
- support is optional. This makes it difficult to implement
- features such as unsolicited disconnect or re-authentication/
- re-authorization on demand across a heterogeneous deployment. To
- address this issue, support for server-initiated messages is
- mandatory in Diameter.
-</pre>
-
-<p>
-A diameter user can both send and receive messages.</p>
-
-<pre>
-
- Transition support
-
- While Diameter does not share a common protocol data unit (PDU)
- with RADIUS, considerable effort has been expended in enabling
- backward compatibility with RADIUS so that the two protocols may
- be deployed in the same network. Initially, it is expected that
- Diameter will be deployed within new network devices, as well as
- within gateways enabling communication between legacy RADIUS
- devices and Diameter agents. This capability enables Diameter
- support to be added to legacy networks, by addition of a gateway
- or server speaking both RADIUS and Diameter.
-</pre>
-
-<p>
-RADIUS Attributes can be redefined as Diameter AVP's using diameter's
-&man_dict; interface but diameter provides no such definitions.</p>
-
-<pre>
-
- In addition to addressing the above requirements, Diameter also
- provides support for the following:
-
- Capability negotiation
-
- RADIUS does not support error messages, capability negotiation, or
- a mandatory/non-mandatory flag for attributes. Since RADIUS
- clients and servers are not aware of each other's capabilities,
- they may not be able to successfully negotiate a mutually
- acceptable service or, in some cases, even be aware of what
- service has been implemented. Diameter includes support for error
- handling (Section 7), capability negotiation (Section 5.3), and
- mandatory/non-mandatory Attribute-Value Pairs (AVPs)
- (Section 4.1).
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 8]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Peer discovery and configuration
-
- RADIUS implementations typically require that the name or address
- of servers or clients be manually configured, along with the
- corresponding shared secrets. This results in a large
- administrative burden and creates the temptation to reuse the
- RADIUS shared secret, which can result in major security
- vulnerabilities if the Request Authenticator is not globally and
- temporally unique as required in [RFC2865]. Through DNS, Diameter
- enables dynamic discovery of peers (see Section 5.2). Derivation
- of dynamic session keys is enabled via transmission-level
- security.
-
- Over time, the capabilities of Network Access Server (NAS) devices
- have increased substantially. As a result, while Diameter is a
- considerably more sophisticated protocol than RADIUS, it remains
- feasible to implement it within embedded devices.
-</pre>
-
-&nada;
-
-<pre>
-
-1.1. Diameter Protocol
-
- The Diameter base protocol provides the following facilities:
-
- o Ability to exchange messages and deliver AVPs
-</pre>
-
-<p>
-There are two interfaces directly involved in message exchange when
-using diameter: the function &mod_call; for sending outgoing requests,
-and the application callback interface, documented in &man_app; for
-receiving incoming request and answers.</p>
-
-<pre>
-
- o Capabilities negotiation
-</pre>
-
-<p>
-Capabilities negotiation is the responsibility of diameter:
-a user configures a diameter service and/or transport with
-&capabilities; to provide AVP values for CER and CEA messages but it
-is diameter itself that sends these messages.
-A user receives notification of a successful capabilities exchange by
-way of &app_peer_up; callbacks.</p>
-
-<pre>
-
- o Error notification
-</pre>
-
-<p>
-A user can subscribe to &events;, using &mod_subscribe;, in order to
-receive notification of various failures.
-Errors in Diameter messaging are communicated via the application
-callbacks &app_handle_request;, &app_handle_answer; and
-&app_handle_error;.</p>
-
-
-<pre>
-
- o Extensibility, required in [RFC2989], through addition of new
- applications, commands, and AVPs
-</pre>
-
-<p>
-Support for applications, commands and AVP's is extensible using
-diameter's dictionary interface, as documented in &man_dict;.
-Dictionaries are compiled to Erlang encode/decode modules using
-&man_compile; or &man_make;.</p>
-
-<pre>
-
- o Basic services necessary for applications, such as the handling of
- user sessions or accounting
-</pre>
-
-<p>
-Compiled dictionaries are provided for the RFC 3588 and RFC 6733
-Diameter applications: common, base accounting and relay.
-Dictionaries for a number of standardized
-applications are provided in uncompiled form below the <c>examples</c>
-subdirectory of the diameter application directory.</p>
-
-<pre>
-
- All data delivered by the protocol is in the form of AVPs. Some of
- these AVP values are used by the Diameter protocol itself, while
- others deliver data associated with particular applications that
- employ Diameter. AVPs may be arbitrarily added to Diameter messages,
- the only restriction being that the Command Code Format (CCF)
- specification (Section 3.2) be satisfied. AVPs are used by the base
- Diameter protocol to support the following required features:
-
- o Transporting of user authentication information, for the purposes
- of enabling the Diameter server to authenticate the user
-
- o Transporting of service-specific authorization information,
- between client and servers, allowing the peers to decide whether a
- user's access request should be granted
-
-
-
-Fajardo, et al. Standards Track [Page 9]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o Exchanging resource usage information, which may be used for
- accounting purposes, capacity planning, etc.
-
- o Routing, relaying, proxying, and redirecting of Diameter messages
- through a server hierarchy
-
- The Diameter base protocol satisfies the minimum requirements for a
- AAA protocol, as specified by [RFC2989]. The base protocol may be
- used by itself for accounting purposes only, or it may be used with a
- Diameter application, such as Mobile IPv4 [RFC4004], or network
- access [RFC4005]. It is also possible for the base protocol to be
- extended for use in new applications, via the addition of new
- commands or AVPs. The initial focus of Diameter was network access
- and accounting applications. A truly generic AAA protocol used by
- many applications might provide functionality not provided by
- Diameter. Therefore, it is imperative that the designers of new
- applications understand their requirements before using Diameter.
- See Section 1.3.4 for more information on Diameter applications.
-
- Any node can initiate a request. In that sense, Diameter is a peer-
- to-peer protocol. In this document, a Diameter client is a device at
- the edge of the network that performs access control, such as a
- Network Access Server (NAS) or a Foreign Agent (FA). A Diameter
- client generates Diameter messages to request authentication,
- authorization, and accounting services for the user. A Diameter
- agent is a node that does not provide local user authentication or
- authorization services; agents include proxies, redirects, and relay
- agents. A Diameter server performs authentication and/or
- authorization of the user. A Diameter node may act as an agent for
- certain requests while acting as a server for others.
-
- The Diameter protocol also supports server-initiated messages, such
- as a request to abort service to a particular user.
-</pre>
-
-&nada;
-
-<pre>
-
-1.1.1. Description of the Document Set
-
- The Diameter specification consists of an updated version of the base
- protocol specification (this document) and the Transport Profile
- [RFC3539]. This document obsoletes both RFC 3588 and RFC 5719. A
- summary of the base protocol updates included in this document can be
- found in Section 1.1.3.
-
- This document defines the base protocol specification for AAA, which
- includes support for accounting. There are also a myriad of
- applications documents describing applications that use this base
- specification for Authentication, Authorization, and Accounting.
- These application documents specify how to use the Diameter protocol
- within the context of their application.
-
-
-
-Fajardo, et al. Standards Track [Page 10]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The Transport Profile document [RFC3539] discusses transport layer
- issues that arise with AAA protocols and recommendations on how to
- overcome these issues. This document also defines the Diameter
- failover algorithm and state machine.
-
- "Clarifications on the Routing of Diameter Request Based on the
- Username and the Realm" [RFC5729] defines specific behavior on how to
- route requests based on the content of the User-Name AVP (Attribute
- Value Pair).
-
-1.1.2. Conventions Used in This Document
-
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
- document are to be interpreted as described in [RFC2119].
-</pre>
-
-&nada;
-
-<pre>
-
-1.1.3. Changes from RFC 3588
-
- This document obsoletes RFC 3588 but is fully backward compatible
- with that document. The changes introduced in this document focus on
- fixing issues that have surfaced during the implementation of
- Diameter (RFC 3588). An overview of some the major changes are given
- below.
-</pre>
-
-<p>
-RFC 6733 is not fully backwards compatible with RFC 3588.
-(For example, in what values of Result-Code values are permissible with
-the E-bit.)
-The implications of incompatibilities for diameter are noted where
-appropriate.</p>
-
-<pre>
-
- o Deprecated the use of the Inband-Security AVP for negotiating
- Transport Layer Security (TLS) [RFC5246]. It has been generally
- considered that bootstrapping of TLS via Inband-Security AVP
- creates certain security risks because it does not completely
- protect the information carried in the CER/CEA (Capabilities-
- Exchange-Request/Capabilities-Exchange-Answer). This version of
- Diameter adopts the common approach of defining a well-known
- secured port that peers should use when communicating via TLS/TCP
- and DTLS/SCTP. This new approach augments the existing in-band
- security negotiation, but it does not completely replace it. The
- old method is kept for backward compatibility reasons.
-</pre>
-
-<p>
-&man_tcp; supports both methods of negotiating TLS:
-bootstrapping via Inband-Security and directly following connection
-establishment.</p>
-
-<pre>
-
- o Deprecated the exchange of CER/CEA messages in the open state.
- This feature was implied in the peer state machine table of RFC
- 3588, but it was not clearly defined anywhere else in that
- document. As work on this document progressed, it became clear
- that the multiplicity of meaning and use of Application-Id AVPs in
- the CER/CEA messages (and the messages themselves) is seen as an
- abuse of the Diameter extensibility rules and thus required
- simplification. Capabilities exchange in the open state has been
- re-introduced in a separate specification [RFC6737], which clearly
- defines new commands for this feature.
-</pre>
-
-<p>
-Capabilities exchange in the open state is not supported: an incoming
-CER in the open state will cause diameter to ask the relevant
-transport process to terminate, which implies the loss of the peer
-connection in the case of &man_tcp; and &man_sctp;.</p>
-
-<p>
-Capabilities update, as defined by RFC 6737, is not yet supported.
-Support will require diameter to handle CUR/CUA in the same way that
-it handles CER/CEA.</p>
-
-<pre>
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 11]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o Simplified security requirements. The use of a secured transport
- for exchanging Diameter messages remains mandatory. However, TLS/
- TCP and DTLS/SCTP have become the primary methods of securing
- Diameter with IPsec as a secondary alternative. See Section 13
- for details. The support for the End-to-End security framework
- (E2E-Sequence AVP and 'P'-bit in the AVP header) has also been
- deprecated.
-</pre>
-
-<p>
-The End-to-End security framework is not supported since it's use is
-largely unspecified: diameter will set the P-bit in outgoing AVP's as
-directed by the relevant dictionary and/or &app_prepare_request; or
-&app_handle_request; callbacks, but whether or not the P-bit is set on
-incoming AVP's has no consequence.</p>
-
-<p>
-As noted above, DTLS is not currently supported and whether or not
-IPsec is used is transparent to diameter.</p>
-
-<pre>
-
- o Changed Diameter extensibility. This includes fixes to the
- Diameter extensibility description (Section 1.3 and others) to
- better aid Diameter application designers; in addition, the new
- specification relaxes the policy with respect to the allocation of
- Command Codes for vendor-specific uses.
-
- o Clarified Application Id usage. Clarify the proper use of
- Application Id information, which can be found in multiple places
- within a Diameter message. This includes correlating Application
- Ids found in the message headers and AVPs. These changes also
- clearly specify the proper Application Id value to use for
- specific base protocol messages (ASR/ASA, STR/STA) as well as
- clarify the content and use of Vendor-Specific-Application-Id.
-
- o Clarified routing fixes. This document more clearly specifies
- what information (AVPs and Application Ids) can be used for making
- general routing decisions. A rule for the prioritization of
- redirect routing criteria when multiple route entries are found
- via redirects has also been added (see Section 6.13).
-
- o Simplified Diameter peer discovery. The Diameter discovery
- process now supports only widely used discovery schemes; the rest
- have been deprecated (see Section 5.2 for details).
-</pre>
-
-<p>
-Peer discover is not currently supported: peers to which a node should
-connect must be configured.
-Connection requests are accepted from arbitrary peers but a
-&mod_transport_opt; <c>capabilities_cb</c> can be used to reject a
-peer based on an incoming CER or CEA.</p>
-
-<pre>
-
- There are many other miscellaneous fixes that have been introduced in
- this document that may not be considered significant, but they have
- value nonetheless. Examples are removal of obsolete types, fixes to
- the state machine, clarification of the election process, message
- validation, fixes to Failed-AVP and Result-Code AVP values, etc. All
- of the errata filed against RFC 3588 prior to the publication of this
- document have been addressed. A comprehensive list of changes is not
- shown here for practical reasons.
-
-1.2. Terminology
-
- AAA
-
- Authentication, Authorization, and Accounting.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 12]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- ABNF
-
- Augmented Backus-Naur Form [RFC5234]. A metalanguage with its own
- formal syntax and rules. It is based on the Backus-Naur Form and
- is used to define message exchanges in a bi-directional
- communications protocol.
-
- Accounting
-
- The act of collecting information on resource usage for the
- purpose of capacity planning, auditing, billing, or cost
- allocation.
-
- Accounting Record
-
- An accounting record represents a summary of the resource
- consumption of a user over the entire session. Accounting servers
- creating the accounting record may do so by processing interim
- accounting events or accounting events from several devices
- serving the same user.
-
- Authentication
-
- The act of verifying the identity of an entity (subject).
-
- Authorization
-
- The act of determining whether a requesting entity (subject) will
- be allowed access to a resource (object).
-
- Attribute-Value Pair (AVP)
-
- The Diameter protocol consists of a header followed by one or more
- Attribute-Value-Pairs (AVPs). An AVP includes a header and is
- used to encapsulate protocol-specific data (e.g., routing
- information) as well as authentication, authorization, or
- accounting information.
-</pre>
-
-&nada;
-
-<pre>
-
- Command Code Format (CCF)
-
- A modified form of ABNF used to define Diameter commands (see
- Section 3.2).
-</pre>
-
-<p>
-The <c>@messages</c> section of the &man_dict; format has the CCF as
-content.</p>
-
-<pre>
-
- Diameter Agent
-
- A Diameter Agent is a Diameter node that provides relay, proxy,
- redirect, or translation services.
-
-
-
-
-Fajardo, et al. Standards Track [Page 13]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Diameter Client
-
- A Diameter client is a Diameter node that supports Diameter client
- applications as well as the base protocol. Diameter clients are
- often implemented in devices situated at the edge of a network and
- provide access control services for that network. Typical
- examples of Diameter clients include the Network Access Server
- (NAS) and the Mobile IP Foreign Agent (FA).
-
- Diameter Node
-
- A Diameter node is a host process that implements the Diameter
- protocol and acts as either a client, an agent, or a server.
-
- Diameter Peer
-
- Two Diameter nodes sharing a direct TCP or SCTP transport
- connection are called Diameter peers.
-
- Diameter Server
-
- A Diameter server is a Diameter node that handles authentication,
- authorization, and accounting requests for a particular realm. By
- its very nature, a Diameter server must support Diameter server
- applications in addition to the base protocol.
-</pre>
-
-<p>
-A Diameter Node is implemented by configuring a service
-using &mod_start_service; and one or more transports using
-&mod_add_transport;.
-The service typically represents a Diameter Node but since
-capabilities can be configured on individual transports it's more
-accurate to say that the node is a collection of transports
-advertising the same Origin-Host.</p>
-
-<p>
-The role of a node (agent, client or server) is not something that's
-configured explicitly.
-Transports are either connecting or listening, depending on whether
-diameter should establish a peer connection and send CER or accept
-connections and receive CER, but the role a node implements depends
-largely on dictionary configuration and &man_app; callback
-implementation.</p>
-
-<pre>
-
- Downstream
-
- Downstream is used to identify the direction of a particular
- Diameter message from the home server towards the Diameter client.
-
- Home Realm
-
- A Home Realm is the administrative domain with which the user
- maintains an account relationship.
-
- Home Server
-
- A Diameter server that serves the Home Realm.
-
- Interim Accounting
-
- An interim accounting message provides a snapshot of usage during
- a user's session. Typically, it is implemented in order to
- provide for partial accounting of a user's session in case a
- device reboot or other network problem prevents the delivery of a
- session summary message or session record.
-
-
-
-
-Fajardo, et al. Standards Track [Page 14]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Local Realm
-
- A local realm is the administrative domain providing services to a
- user. An administrative domain may act as a local realm for
- certain users while being a home realm for others.
-
- Multi-session
-
- A multi-session represents a logical linking of several sessions.
- Multi-sessions are tracked by using the Acct-Multi-Session-Id. An
- example of a multi-session would be a Multi-link PPP bundle. Each
- leg of the bundle would be a session while the entire bundle would
- be a multi-session.
-
- Network Access Identifier
-
- The Network Access Identifier, or NAI [RFC4282], is used in the
- Diameter protocol to extract a user's identity and realm. The
- identity is used to identify the user during authentication and/or
- authorization while the realm is used for message routing
- purposes.
-
- Proxy Agent or Proxy
-
- In addition to forwarding requests and responses, proxies make
- policy decisions relating to resource usage and provisioning.
- Typically, this is accomplished by tracking the state of NAS
- devices. While proxies usually do not respond to client requests
- prior to receiving a response from the server, they may originate
- Reject messages in cases where policies are violated. As a
- result, proxies need to understand the semantics of the messages
- passing through them, and they may not support all Diameter
- applications.
-
- Realm
-
- The string in the NAI that immediately follows the '@' character.
- NAI realm names are required to be unique and are piggybacked on
- the administration of the DNS namespace. Diameter makes use of
- the realm, also loosely referred to as domain, to determine
- whether messages can be satisfied locally or whether they must be
- routed or redirected. In RADIUS, realm names are not necessarily
- piggybacked on the DNS namespace but may be independent of it.
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 15]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Real-Time Accounting
-
- Real-time accounting involves the processing of information on
- resource usage within a defined time window. Typically, time
- constraints are imposed in order to limit financial risk. The
- Diameter Credit-Control Application [RFC4006] is an example of an
- application that defines real-time accounting functionality.
-
- Relay Agent or Relay
-
- Relays forward requests and responses based on routing-related
- AVPs and routing table entries. Since relays do not make policy
- decisions, they do not examine or alter non-routing AVPs. As a
- result, relays never originate messages, do not need to understand
- the semantics of messages or non-routing AVPs, and are capable of
- handling any Diameter application or message type. Since relays
- make decisions based on information in routing AVPs and realm
- forwarding tables, they do not keep state on NAS resource usage or
- sessions in progress.
-
- Redirect Agent
-
- Rather than forwarding requests and responses between clients and
- servers, redirect agents refer clients to servers and allow them
- to communicate directly. Since redirect agents do not sit in the
- forwarding path, they do not alter any AVPs transiting between
- client and server. Redirect agents do not originate messages and
- are capable of handling any message type, although they may be
- configured only to redirect messages of certain types, while
- acting as relay or proxy agents for other types. As with relay
- agents, redirect agents do not keep state with respect to sessions
- or NAS resources.
-</pre>
-
-&nada;
-
-<pre>
-
- Session
-
- A session is a related progression of events devoted to a
- particular activity. Diameter application documents provide
- guidelines as to when a session begins and ends. All Diameter
- packets with the same Session-Id are considered to be part of the
- same session.
-</pre>
-
-<p>
-Sessions are not something that diameter is aware of.
-The function &mod_session_id; can be used to construct appropriate
-values for Session-Id AVP's but logic connecting events in the same
-session is the responsibility of the diameter user.</p>
-
-<pre>
-
- Stateful Agent
-
- A stateful agent is one that maintains session state information,
- by keeping track of all authorized active sessions. Each
- authorized session is bound to a particular service, and its state
- is considered active either until it is notified otherwise or
- until expiration.
-
-
-
-Fajardo, et al. Standards Track [Page 16]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Sub-session
-
- A sub-session represents a distinct service (e.g., QoS or data
- characteristics) provided to a given session. These services may
- happen concurrently (e.g., simultaneous voice and data transfer
- during the same session) or serially. These changes in sessions
- are tracked with the Accounting-Sub-Session-Id.
-
- Transaction State
-
- The Diameter protocol requires that agents maintain transaction
- state, which is used for failover purposes. Transaction state
- implies that upon forwarding a request, the Hop-by-Hop Identifier
- is saved; the field is replaced with a locally unique identifier,
- which is restored to its original value when the corresponding
- answer is received. The request's state is released upon receipt
- of the answer. A stateless agent is one that only maintains
- transaction state.
-
- Translation Agent
-
- A translation agent (TLA in Figure 4) is a stateful Diameter node
- that performs protocol translation between Diameter and another
- AAA protocol, such as RADIUS.
-
- Upstream
-
- Upstream is used to identify the direction of a particular
- Diameter message from the Diameter client towards the home server.
-
- User
-
- The entity or device requesting or using some resource, in support
- of which a Diameter client has generated a request.
-</pre>
-
-&nada;
-
-<pre>
-
-1.3. Approach to Extensibility
-
- The Diameter protocol is designed to be extensible, using several
- mechanisms, including:
-
- o Defining new AVP values
-
- o Creating new AVPs
-
- o Creating new commands
-
- o Creating new applications
-
-
-
-
-Fajardo, et al. Standards Track [Page 17]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- From the point of view of extensibility, Diameter authentication,
- authorization, and accounting applications are treated in the same
- way.
-</pre>
-
-<p>
-Extensibility in diameter is by way of the dictionary interface
-documented in &man_dict;: a diameter user creates applications,
-commands and AVP's by implementing a new dictionary,
-compiling the dictionary to a codec module using &man_compile; or
-&man_make;, and configuring the resulting dictionary module on a
-service.
-The dictionary modules provided with diameter are all implemented in
-this manner.</p>
-
-<pre>
- Note: Protocol designers should try to reuse existing functionality,
- namely AVP values, AVPs, commands, and Diameter applications. Reuse
- simplifies standardization and implementation. To avoid potential
- interoperability issues, it is important to ensure that the semantics
- of the reused features are well understood. Given that Diameter can
- also carry RADIUS attributes as Diameter AVPs, such reuse
- considerations also apply to existing RADIUS attributes that may be
- useful in a Diameter application.
-</pre>
-
-<p>
-Reuse in dictionary files is achieved by way of the <c>@inherits</c>
-section.
-AVP's are inherited, commands are not.</p>
-
-<pre>
-
-1.3.1. Defining New AVP Values
-
- In order to allocate a new AVP value for AVPs defined in the Diameter
- base protocol, the IETF needs to approve a new RFC that describes the
- AVP value. IANA considerations for these AVP values are discussed in
- Section 11.3.
-
- The allocation of AVP values for other AVPs is guided by the IANA
- considerations of the document that defines those AVPs. Typically,
- allocation of new values for an AVP defined in an RFC would require
- IETF Review [RFC5226], whereas values for vendor-specific AVPs can be
- allocated by the vendor.
-
-1.3.2. Creating New AVPs
-
- A new AVP being defined MUST use one of the data types listed in
- Sections 4.2 or 4.3. If an appropriate derived data type is already
- defined, it SHOULD be used instead of a base data type to encourage
- reusability and good design practice.
-
- In the event that a logical grouping of AVPs is necessary, and
- multiple "groups" are possible in a given command, it is recommended
- that a Grouped AVP be used (see Section 4.4).
-
- The creation of new AVPs can happen in various ways. The recommended
- approach is to define a new general-purpose AVP in a Standards Track
- RFC approved by the IETF. However, as described in Section 11.1.1,
- there are other mechanisms.
-</pre>
-
-<p>
-Creating new AVP's is an issue for the dictionary designer, not
-diameter.</p>
-
-<pre>
-
-1.3.3. Creating New Commands
-
- A new Command Code MUST be allocated when required AVPs (those
- indicated as {AVP} in the CCF definition) are added to, deleted from,
- or redefined in (for example, by changing a required AVP into an
- optional one) an existing command.
-
-
-
-Fajardo, et al. Standards Track [Page 18]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Furthermore, if the transport characteristics of a command are
- changed (for example, with respect to the number of round trips
- required), a new Command Code MUST be registered.
-
- A change to the CCF of a command, such as described above, MUST
- result in the definition of a new Command Code. This subsequently
- leads to the need to define a new Diameter application for any
- application that will use that new command.
-
- The IANA considerations for Command Codes are discussed in
- Section 3.1.
-</pre>
-
-<p>
-Creating new commands is an issue for the dictionary designer, not
-diameter.</p>
-
-<pre>
-
-1.3.4. Creating New Diameter Applications
-
- Every Diameter application specification MUST have an IANA-assigned
- Application Id (see Section 2.4). The managed Application ID space
- is flat, and there is no relationship between different Diameter
- applications with respect to their Application Ids. As such, there
- is no versioning support provided by these Application Ids
- themselves; every Diameter application is a standalone application.
- If the application has a relationship with other Diameter
- applications, such a relationship is not known to Diameter.
-</pre>
-
-<p>
-Creating new applications is an issue for the dictionary designer,
-not diameter.</p>
-
-<p>
-An application's Application Id is specified in the <c>@id</c> section
-of a dictionary file.</p>
-
-<pre>
-
- Before describing the rules for creating new Diameter applications,
- it is important to discuss the semantics of the AVP occurrences as
- stated in the CCF and the M-bit flag (Section 4.1) for an AVP. There
- is no relationship imposed between the two; they are set
- independently.
-
- o The CCF indicates what AVPs are placed into a Diameter command by
- the sender of that command. Often, since there are multiple modes
- of protocol interactions, many of the AVPs are indicated as
- optional.
-
- o The M-bit allows the sender to indicate to the receiver whether or
- not understanding the semantics of an AVP and its content is
- mandatory. If the M-bit is set by the sender and the receiver
- does not understand the AVP or the values carried within that AVP,
- then a failure is generated (see Section 7).
-</pre>
-
-<p>
-The M-bit is set on outgoing AVP's as directed by the relevant
-dictionary.
-For incoming AVP's, an M-bit set on an AVP that isn't
-explicitly included in the definition of the command in question is
-interpreted as a 5001 error, DIAMETER_AVP_UNSUPPORTED, the
-consequences of which depend on the value of the &mod_application_opt;
-<c>answer_errors</c> or <c>request_errors</c>.</p>
-
-<pre>
-
- It is the decision of the protocol designer when to develop a new
- Diameter application rather than extending Diameter in other ways.
- However, a new Diameter application MUST be created when one or more
- of the following criteria are met:
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 19]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- M-bit Setting
-
- An AVP with the M-bit in the MUST column of the AVP flag table is
- added to an existing Command/Application. An AVP with the M-bit
- in the MAY column of the AVP flag table is added to an existing
- Command/Application.
-
- Note: The M-bit setting for a given AVP is relevant to an
- Application and each command within that application that includes
- the AVP. That is, if an AVP appears in two commands for
- application Foo and the M-bit settings are different in each
- command, then there should be two AVP flag tables describing when
- to set the M-bit.
-
- Commands
-
- A new command is used within the existing application because
- either an additional command is added, an existing command has
- been modified so that a new Command Code had to be registered, or
- a command has been deleted.
-
- AVP Flag bits
-
- If an existing application changes the meaning/semantics of its
- AVP Flags or adds new flag bits, then a new Diameter application
- MUST be created.
-
- If the CCF definition of a command allows it, an implementation may
- add arbitrary optional AVPs with the M-bit cleared (including vendor-
- specific AVPs) to that command without needing to define a new
- application. Please refer to Section 11.1.1 for details.
-</pre>
-
-&nada;
-
-<pre>
-
-2. Protocol Overview
-
- The base Diameter protocol concerns itself with establishing
- connections to peers, capabilities negotiation, how messages are sent
- and routed through peers, and how the connections are eventually torn
- down. The base protocol also defines certain rules that apply to all
- message exchanges between Diameter nodes.
-
- Communication between Diameter peers begins with one peer sending a
- message to another Diameter peer. The set of AVPs included in the
- message is determined by a particular Diameter application. One AVP
- that is included to reference a user's session is the Session-Id.
-
- The initial request for authentication and/or authorization of a user
- would include the Session-Id AVP. The Session-Id is then used in all
- subsequent messages to identify the user's session (see Section 8 for
-
-
-
-Fajardo, et al. Standards Track [Page 20]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- more information). The communicating party may accept the request or
- reject it by returning an answer message with the Result-Code AVP set
- to indicate that an error occurred. The specific behavior of the
- Diameter server or client receiving a request depends on the Diameter
- application employed.
-
- Session state (associated with a Session-Id) MUST be freed upon
- receipt of the Session-Termination-Request, Session-Termination-
- Answer, expiration of authorized service time in the Session-Timeout
- AVP, and according to rules established in a particular Diameter
- application.
-</pre>
-
-<p>
-Like Session-Id, session state is maintained by the diameter user:
-diameter has no session state of its own and does not interpret
-STR/STA in any way.</p>
-
-<pre>
-
- The base Diameter protocol may be used by itself for accounting
- applications. For authentication and authorization, it is always
- extended for a particular application.
-
- Diameter clients MUST support the base protocol, which includes
- accounting. In addition, they MUST fully support each Diameter
- application that is needed to implement the client's service, e.g.,
- Network Access Server Requirements (NASREQ) [RFC2881] and/or Mobile
- IPv4. A Diameter client MUST be referred to as "Diameter X Client"
- where X is the application that it supports and not a "Diameter
- Client".
-
- Diameter servers MUST support the base protocol, which includes
- accounting. In addition, they MUST fully support each Diameter
- application that is needed to implement the intended service, e.g.,
- NASREQ and/or Mobile IPv4. A Diameter server MUST be referred to as
- "Diameter X Server" where X is the application that it supports, and
- not a "Diameter Server".
-
- Diameter relays and redirect agents are transparent to the Diameter
- applications, but they MUST support the Diameter base protocol, which
- includes accounting, and all Diameter applications.
-
- Diameter proxies MUST support the base protocol, which includes
- accounting. In addition, they MUST fully support each Diameter
- application that is needed to implement proxied services, e.g.,
- NASREQ and/or Mobile IPv4. A Diameter proxy MUST be referred to as
- "Diameter X Proxy" where X is the application which it supports, and
- not a "Diameter Proxy".
-
-</pre>
-
-&nada;
-
-<pre>
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 21]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-2.1. Transport
-
- The Diameter Transport profile is defined in [RFC3539].
-
- The base Diameter protocol is run on port 3868 for both TCP [RFC0793]
- and SCTP [RFC4960]. For TLS [RFC5246] and Datagram Transport Layer
- Security (DTLS) [RFC6347], a Diameter node that initiates a
- connection prior to any message exchanges MUST run on port 5658. It
- is assumed that TLS is run on top of TCP when it is used, and DTLS is
- run on top of SCTP when it is used.
-</pre>
-
-<p>
-Which port a transport connects to or listens on is a matter of
-configuration.
-Both &man_tcp; and &man_sctp; will default to 3868 if no other value
-is specified.</p>
-
-<pre>
-
- If the Diameter peer does not support receiving TLS/TCP and DTLS/SCTP
- connections on port 5658 (i.e., the peer complies only with RFC
- 3588), then the initiator MAY revert to using TCP or SCTP on port
- 3868. Note that this scheme is kept only for the purpose of backward
- compatibility and that there are inherent security vulnerabilities
- when the initial CER/CEA messages are sent unprotected (see
- Section 5.6).
-
- Diameter clients MUST support either TCP or SCTP; agents and servers
- SHOULD support both.
-
- A Diameter node MAY initiate connections from a source port other
- than the one that it declares it accepts incoming connections on, and
- it MUST always be prepared to receive connections on port 3868 for
- TCP or SCTP and port 5658 for TLS/TCP and DTLS/SCTP connections.
- When DNS-based peer discovery (Section 5.2) is used, the port numbers
- received from SRV records take precedence over the default ports
- (3868 and 5658).
-
- A given Diameter instance of the peer state machine MUST NOT use more
- than one transport connection to communicate with a given peer,
- unless multiple instances exist on the peer, in which, case a
- separate connection per process is allowed.
-</pre>
-
-<p>
-The &mod_service_opt; <c>restrict_connection</c> controls to what
-extent a diameter service allows multiple connections to the same
-peer.
-(As identified by the value of Origin-Host received from it
-during capabilities exchange.)</p>
-
-<pre>
-
- When no transport connection exists with a peer, an attempt to
- connect SHOULD be made periodically. This behavior is handled via
- the Tc timer (see Section 12 for details), whose recommended value is
- 30 seconds. There are certain exceptions to this rule, such as when
- a peer has terminated the transport connection stating that it does
- not wish to communicate.
-
-</pre>
-
-<p>
-The frequency of reconnection attempts is configured with the
-&mod_transport_opt; <c>connect_timer</c> and
-<c>watchdog_timer</c>.</p>
-
-<pre>
-
- When connecting to a peer and either zero or more transports are
- specified, TLS SHOULD be tried first, followed by DTLS, then by TCP,
- and finally by SCTP. See Section 5.2 for more information on peer
- discovery.
-</pre>
-
-<p>
-The order in which different transports are attempted depends on the
-order of &mod_transport_opt; <c>transport_module</c> and
-<c>transport_config</c> tuples in transport configuration.</p>
-
-<pre>
-
-
-
-Fajardo, et al. Standards Track [Page 22]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Diameter implementations SHOULD be able to interpret ICMP protocol
- port unreachable messages as explicit indications that the server is
- not reachable, subject to security policy on trusting such messages.
- Further guidance regarding the treatment of ICMP errors can be found
- in [RFC5927] and [RFC5461]. Diameter implementations SHOULD also be
- able to interpret a reset from the transport and timed-out connection
- attempts. If Diameter receives data from the lower layer that cannot
- be parsed or identified as a Diameter error made by the peer, the
- stream is compromised and cannot be recovered. The transport
- connection MUST be closed using a RESET call (send a TCP RST bit) or
- an SCTP ABORT message (graceful closure is compromised).
-</pre>
-
-<p>
-ICMP messages and other transport-level errors aren't directly
-visible to diameter but transport implementations like &man_tcp; and
-&man_sctp; propagate these as terminating transport processes.</p>
-
-<pre>
-
-2.1.1. SCTP Guidelines
-
- Diameter messages SHOULD be mapped into SCTP streams in a way that
- avoids head-of-the-line (HOL) blocking. Among different ways of
- performing the mapping that fulfill this requirement it is
- RECOMMENDED that a Diameter node send every Diameter message (request
- or response) over stream zero with the unordered flag set. However,
- Diameter nodes MAY select and implement other design alternatives for
- avoiding HOL blocking such as using multiple streams with the
- unordered flag cleared (as originally instructed in RFC 3588). On
- the receiving side, a Diameter entity MUST be ready to receive
- Diameter messages over any stream, and it is free to return responses
- over a different stream. This way, both sides manage the available
- streams in the sending direction, independently of the streams chosen
- by the other side to send a particular Diameter message. These
- messages can be out-of-order and belong to different Diameter
- sessions.
-</pre>
-
-<p>
-&man_sctp; allows the sender to specify a stream number explicitly.
-The stream on which an incoming message is received it passed to
-&app_handle_request; and &app_handle_answer; callbacks as
-<c>transport_data</c> in a <c>#diameter_packet{}</c>.</p>
-
-<p>
-Ordered or unordered delivery can be configured per transport.</p>
-
-<pre>
-
- Out-of-order delivery has special concerns during a connection
- establishment and termination. When a connection is established, the
- responder side sends a CEA message and moves to R-Open state as
- specified in Section 5.6. If an application message is sent shortly
- after the CEA and delivered out-of-order, the initiator side, still
- in Wait-I-CEA state, will discard the application message and close
- the connection. In order to avoid this race condition, the receiver
- side SHOULD NOT use out-of-order delivery methods until the first
- message has been received from the initiator, proving that it has
- moved to I-Open state. To trigger such a message, the receiver side
- could send a DWR immediately after sending a CEA. Upon reception of
- the corresponding DWA, the receiver side should start using out-of-
- order delivery methods to counter the HOL blocking.
-</pre>
-
-<p>
-&man_sctp; does not currently allow the user to switch between ordered
-and unordered delivery, or to specify the manner of sending per
-message: one or the other must be configured, the defaults being
-ordered.</p>
-
-<pre>
-
- Another race condition may occur when DPR and DPA messages are used.
- Both DPR and DPA are small in size; thus, they may be delivered to
- the peer faster than application messages when an out-of-order
- delivery mechanism is used. Therefore, it is possible that a DPR/DPA
-
-
-
-Fajardo, et al. Standards Track [Page 23]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- exchange completes while application messages are still in transit,
- resulting in a loss of these messages. An implementation could
- mitigate this race condition, for example, using timers, and wait for
- a short period of time for pending application level messages to
- arrive before proceeding to disconnect the transport connection.
- Eventually, lost messages are handled by the retransmission mechanism
- described in Section 5.5.4.
-
- A Diameter agent SHOULD use dedicated payload protocol identifiers
- (PPIDs) for clear text and encrypted SCTP DATA chunks instead of only
- using the unspecified payload protocol identifier (value 0). For
- this purpose, two PPID values are allocated: the PPID value 46 is for
- Diameter messages in clear text SCTP DATA chunks, and the PPID value
- 47 is for Diameter messages in protected DTLS/SCTP DATA chunks.
-</pre>
-
-&nada;
-
-<pre>
-
-2.2. Securing Diameter Messages
-
- Connections between Diameter peers SHOULD be protected by TLS/TCP and
- DTLS/SCTP. All Diameter base protocol implementations MUST support
- the use of TLS/TCP and DTLS/SCTP. If desired, alternative security
- mechanisms that are independent of Diameter, such as IPsec [RFC4301],
- can be deployed to secure connections between peers. The Diameter
- protocol MUST NOT be used without one of TLS, DTLS, or IPsec.
-</pre>
-
-<p>
-As noted above, DTLS is not currently supported and IPsec usage is
-transparent to diameter.
-Security is not enforced by diameter.</p>
-
-<pre>
-
-2.3. Diameter Application Compliance
-
- Application Ids are advertised during the capabilities exchange phase
- (see Section 5.3). Advertising support of an application implies
- that the sender supports the functionality specified in the
- respective Diameter application specification.
-
- Implementations MAY add arbitrary optional AVPs with the M-bit
- cleared (including vendor-specific AVPs) to a command defined in an
- application, but only if the command's CCF syntax specification
- allows for it. Please refer to Section 11.1.1 for details.
-</pre>
-
-&nada;
-
-<pre>
-
-2.4. Application Identifiers
-
- Each Diameter application MUST have an IANA-assigned Application ID.
- The base protocol does not require an Application Id since its
- support is mandatory. During the capabilities exchange, Diameter
- nodes inform their peers of locally supported applications.
- Furthermore, all Diameter messages contain an Application Id, which
- is used in the message forwarding process.
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 24]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The following Application Id values are defined:
-
- Diameter common message 0
- Diameter base accounting 3
- Relay 0xffffffff
-</pre>
-
-<p>
-These applications are implemented in the dictionary modules
-<c>diameter_gen_base_rfc6733</c>, <c>diameter_gen_acct_rfc6733</c> and
-<c>diameter_relay</c> respectively.
-There are also RFC 3588 versions or the common and accounting
-dictionaries: <c>diameter_gen_base_rfc3588</c> and
-<c>diameter_base_accounting</c>.
-(The inconsistent naming is historical.)
-Dictionary modules are configured using the &mod_application_opt;
-<c>dictionary</c>.</p>
-
-<pre>
- Relay and redirect agents MUST advertise the Relay Application ID,
- while all other Diameter nodes MUST advertise locally supported
- applications. The receiver of a Capabilities Exchange message
- advertising relay service MUST assume that the sender supports all
- current and future applications.
-
- Diameter relay and proxy agents are responsible for finding an
- upstream server that supports the application of a particular
- message. If none can be found, an error message is returned with the
- Result-Code AVP set to DIAMETER_UNABLE_TO_DELIVER.
-</pre>
-
-&nada;
-
-<pre>
-
-2.5. Connections vs. Sessions
-
- This section attempts to provide the reader with an understanding of
- the difference between "connection" and "session", which are terms
- used extensively throughout this document.
-
- A connection refers to a transport-level connection between two peers
- that is used to send and receive Diameter messages. A session is a
- logical concept at the application layer that exists between the
- Diameter client and the Diameter server; it is identified via the
- Session-Id AVP.
-
- +--------+ +-------+ +--------+
- | Client | | Relay | | Server |
- +--------+ +-------+ +--------+
- &lt;----------> &lt;---------->
- peer connection A peer connection B
-
- &lt;----------------------------->
- User session x
-
- Figure 1: Diameter Connections and Sessions
-
- In the example provided in Figure 1, peer connection A is established
- between the client and the relay. Peer connection B is established
- between the relay and the server. User session X spans from the
- client via the relay to the server. Each "user" of a service causes
- an auth request to be sent, with a unique session identifier. Once
- accepted by the server, both the client and the server are aware of
- the session.
-
-
-
-
-Fajardo, et al. Standards Track [Page 25]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- It is important to note that there is no relationship between a
- connection and a session, and that Diameter messages for multiple
- sessions are all multiplexed through a single connection. Also, note
- that Diameter messages pertaining to the session, both application-
- specific and those that are defined in this document such as ASR/ASA,
- RAR/RAA, and STR/STA, MUST carry the Application Id of the
- application. Diameter messages pertaining to peer connection
- establishment and maintenance such as CER/CEA, DWR/DWA, and DPR/DPA
- MUST carry an Application Id of zero (0).
-</pre>
-
-<p>
-As noted above, diameter is not involved in session management.
-This is the responsibility of the diameter user.</p>
-
-<pre>
-
-2.6. Peer Table
-
- The Diameter peer table is used in message forwarding and is
- referenced by the routing table. A peer table entry contains the
- following fields:
-
- Host Identity
-
- Following the conventions described for the DiameterIdentity-
- derived AVP data format in Section 4.3.1, this field contains the
- contents of the Origin-Host (Section 6.3) AVP found in the CER or
- CEA message.
-
- StatusT
-
- This is the state of the peer entry, and it MUST match one of the
- values listed in Section 5.6.
-
- Static or Dynamic
-
- Specifies whether a peer entry was statically configured or
- dynamically discovered.
-
- Expiration Time
-
- Specifies the time at which dynamically discovered peer table
- entries are to be either refreshed or expired. If public key
- certificates are used for Diameter security (e.g., with TLS), this
- value MUST NOT be greater than the expiry times in the relevant
- certificates.
-
- TLS/TCP and DTLS/SCTP Enabled
-
- Specifies whether TLS/TCP and DTLS/SCTP is to be used when
- communicating with the peer.
-
- Additional security information, when needed (e.g., keys,
- certificates).
-</pre>
-
-<p>
-The Peer Table is not directly accessible to the diameter user.
-Information about connected peers can be retrieved using
-&mod_service_info;.</p>
-
-<pre>
-
-
-
-Fajardo, et al. Standards Track [Page 26]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-2.7. Routing Table
-
- All Realm-Based routing lookups are performed against what is
- commonly known as the routing table (see Section 12). Each routing
- table entry contains the following fields:
-
- Realm Name
-
- This is the field that MUST be used as a primary key in the
- routing table lookups. Note that some implementations perform
- their lookups based on longest-match-from-the-right on the realm
- rather than requiring an exact match.
-
- Application Identifier
-
- An application is identified by an Application Id. A route entry
- can have a different destination based on the Application Id in
- the message header. This field MUST be used as a secondary key
- field in routing table lookups.
-
- Local Action
-
- The Local Action field is used to identify how a message should be
- treated. The following actions are supported:
-
- 1. LOCAL - Diameter messages that can be satisfied locally and do
- not need to be routed to another Diameter entity.
-
- 2. RELAY - All Diameter messages that fall within this category
- MUST be routed to a next-hop Diameter entity that is indicated
- by the identifier described below. Routing is done without
- modifying any non-routing AVPs. See Section 6.1.9 for
- relaying guidelines.
-
- 3. PROXY - All Diameter messages that fall within this category
- MUST be routed to a next Diameter entity that is indicated by
- the identifier described below. The local server MAY apply
- its local policies to the message by including new AVPs to the
- message prior to routing. See Section 6.1.9 for proxying
- guidelines.
-
- 4. REDIRECT - Diameter messages that fall within this category
- MUST have the identity of the home Diameter server(s)
- appended, and returned to the sender of the message. See
- Section 6.1.8 for redirection guidelines.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 27]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Server Identifier
-
- The identity of one or more servers to which the message is to be
- routed. This identity MUST also be present in the Host Identity
- field of the peer table (Section 2.6). When the Local Action is
- set to RELAY or PROXY, this field contains the identity of the
- server(s) to which the message MUST be routed. When the Local
- Action field is set to REDIRECT, this field contains the identity
- of one or more servers to which the message MUST be redirected.
-
- Static or Dynamic
-
- Specifies whether a route entry was statically configured or
- dynamically discovered.
-
- Expiration Time
-
- Specifies the time at which a dynamically discovered route table
- entry expires. If public key certificates are used for Diameter
- security (e.g., with TLS), this value MUST NOT be greater than the
- expiry time in the relevant certificates.
-
- It is important to note that Diameter agents MUST support at least
- one of the LOCAL, RELAY, PROXY, or REDIRECT modes of operation.
- Agents do not need to support all modes of operation in order to
- conform with the protocol specification, but they MUST follow the
- protocol compliance guidelines in Section 2. Relay agents and
- proxies MUST NOT reorder AVPs.
-
- The routing table MAY include a default entry that MUST be used for
- any requests not matching any of the other entries. The routing
- table MAY consist of only such an entry.
-
- When a request is routed, the target server MUST have advertised the
- Application Id (see Section 2.4) for the given message or have
- advertised itself as a relay or proxy agent. Otherwise, an error is
- returned with the Result-Code AVP set to DIAMETER_UNABLE_TO_DELIVER.
-</pre>
-
-<p>
-Routing does not need specific support in diameter: a user can
-maintain their own routing table if desired and implement any desired
-routing in &man_app; callbacks.
-However, it may be convenient to add more specific routing support to
-diameter in the future.</p>
-
-<pre>
-
-2.8. Role of Diameter Agents
-
- In addition to clients and servers, the Diameter protocol introduces
- relay, proxy, redirect, and translation agents, each of which is
- defined in Section 1.2. Diameter agents are useful for several
- reasons:
-</pre>
-
-<p>
-An noted above, the role a node plays is largely a question of
-configuration and &man_app; callback implementation.</p>
-
-<pre>
-
- o They can distribute administration of systems to a configurable
- grouping, including the maintenance of security associations.
-
-
-
-
-Fajardo, et al. Standards Track [Page 28]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o They can be used for concentration of requests from a number of
- co-located or distributed NAS equipment sets to a set of like user
- groups.
-
- o They can do value-added processing to the requests or responses.
-
- o They can be used for load balancing.
-
- o A complex network will have multiple authentication sources, they
- can sort requests and forward towards the correct target.
-
- The Diameter protocol requires that agents maintain transaction
- state, which is used for failover purposes. Transaction state
- implies that upon forwarding a request, its Hop-by-Hop Identifier is
- saved; the field is replaced with a locally unique identifier, which
- is restored to its original value when the corresponding answer is
- received. The request's state is released upon receipt of the
- answer. A stateless agent is one that only maintains transaction
- state.
-
- The Proxy-Info AVP allows stateless agents to add local state to a
- Diameter request, with the guarantee that the same state will be
- present in the answer. However, the protocol's failover procedures
- require that agents maintain a copy of pending requests.
-
- A stateful agent is one that maintains session state information by
- keeping track of all authorized active sessions. Each authorized
- session is bound to a particular service, and its state is considered
- active until either the agent is notified otherwise or the session
- expires. Each authorized session has an expiration, which is
- communicated by Diameter servers via the Session-Timeout AVP.
-
- Maintaining session state may be useful in certain applications, such
- as:
-
- o Protocol translation (e.g., RADIUS &lt;-> Diameter)
-
- o Limiting resources authorized to a particular user
-
- o Per-user or per-transaction auditing
-
- A Diameter agent MAY act in a stateful manner for some requests and
- be stateless for others. A Diameter implementation MAY act as one
- type of agent for some requests and as another type of agent for
- others.
-</pre>
-
-&nada;
-
-<pre>
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 29]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-2.8.1. Relay Agents
-
- Relay agents are Diameter agents that accept requests and route
- messages to other Diameter nodes based on information found in the
- messages (e.g., the value of the Destination-Realm AVP Section 6.6).
- This routing decision is performed using a list of supported realms
- and known peers. This is known as the routing table, as is defined
- further in Section 2.7.
-
- Relays may, for example, be used to aggregate requests from multiple
- Network Access Servers (NASes) within a common geographical area
- (Point of Presence, POP). The use of relays is advantageous since it
- eliminates the need for NASes to be configured with the necessary
- security information they would otherwise require to communicate with
- Diameter servers in other realms. Likewise, this reduces the
- configuration load on Diameter servers that would otherwise be
- necessary when NASes are added, changed, or deleted.
-
- Relays modify Diameter messages by inserting and removing routing
- information, but they do not modify any other portion of a message.
- Relays SHOULD NOT maintain session state but MUST maintain
- transaction state.
-
- +------+ ---------> +------+ ---------> +------+
- | | 1. Request | | 2. Request | |
- | NAS | | DRL | | HMS |
- | | 4. Answer | | 3. Answer | |
- +------+ &lt;--------- +------+ &lt;--------- +------+
- example.net example.net example.com
-
- Figure 2: Relaying of Diameter messages
-
- The example provided in Figure 2 depicts a request issued from a NAS,
- which is an access device, for the user [email protected]. Prior to
- issuing the request, the NAS performs a Diameter route lookup, using
- "example.com" as the key, and determines that the message is to be
- relayed to a DRL, which is a Diameter relay. The DRL performs the
- same route lookup as the NAS, and relays the message to the HMS,
- which is example.com's home server. The HMS identifies that the
- request can be locally supported (via the realm), processes the
- authentication and/or authorization request, and replies with an
- answer, which is routed back to the NAS using saved transaction
- state.
-
- Since relays do not perform any application-level processing, they
- provide relaying services for all Diameter applications; therefore,
- they MUST advertise the Relay Application Id.
-</pre>
-
-<p>
-Requests are relayed by returning a <c>relay</c> tuple from a
-&app_handle_request; callback.</p>
-
-<pre>
-
-
-
-Fajardo, et al. Standards Track [Page 30]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-2.8.2. Proxy Agents
-
- Similar to relays, proxy agents route Diameter messages using the
- Diameter routing table. However, they differ since they modify
- messages to implement policy enforcement. This requires that proxies
- maintain the state of their downstream peers (e.g., access devices)
- to enforce resource usage, provide admission control, and provide
- provisioning.
-
- Proxies may, for example, be used in call control centers or access
- ISPs that provide outsourced connections; they can monitor the number
- and type of ports in use and make allocation and admission decisions
- according to their configuration.
-
- Since enforcing policies requires an understanding of the service
- being provided, proxies MUST only advertise the Diameter applications
- they support.
-</pre>
-
-&nada;
-
-<pre>
-
-2.8.3. Redirect Agents
-
- Redirect agents are useful in scenarios where the Diameter routing
- configuration needs to be centralized. An example is a redirect
- agent that provides services to all members of a consortium, but does
- not wish to be burdened with relaying all messages between realms.
- This scenario is advantageous since it does not require that the
- consortium provide routing updates to its members when changes are
- made to a member's infrastructure.
-
- Since redirect agents do not relay messages, and only return an
- answer with the information necessary for Diameter agents to
- communicate directly, they do not modify messages. Since redirect
- agents do not receive answer messages, they cannot maintain session
- state.
-
- The example provided in Figure 3 depicts a request issued from the
- access device, NAS, for the user [email protected]. The message is
- forwarded by the NAS to its relay, DRL, which does not have a routing
- entry in its Diameter routing table for example.com. The DRL has a
- default route configured to DRD, which is a redirect agent that
- returns a redirect notification to DRL, as well as the HMS' contact
- information. Upon receipt of the redirect notification, the DRL
- establishes a transport connection with the HMS, if one doesn't
- already exist, and forwards the request to it.
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 31]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- +------+
- | |
- | DRD |
- | |
- +------+
- ^ |
- 2. Request | | 3. Redirection
- | | Notification
- | v
- +------+ ---------> +------+ ---------> +------+
- | | 1. Request | | 4. Request | |
- | NAS | | DRL | | HMS |
- | | 6. Answer | | 5. Answer | |
- +------+ &lt;--------- +------+ &lt;--------- +------+
- example.net example.net example.com
-
- Figure 3: Redirecting a Diameter Message
-
- Since redirect agents do not perform any application-level
- processing, they provide relaying services for all Diameter
- applications; therefore, they MUST advertise the Relay Application
- ID.
-</pre>
-
-&nada;
-
-<pre>
-
-2.8.4. Translation Agents
-
- A translation agent is a device that provides translation between two
- protocols (e.g., RADIUS&lt;->Diameter, TACACS+&lt;->Diameter). Translation
- agents are likely to be used as aggregation servers to communicate
- with a Diameter infrastructure, while allowing for the embedded
- systems to be migrated at a slower pace.
-
- Given that the Diameter protocol introduces the concept of long-lived
- authorized sessions, translation agents MUST be session stateful and
- MUST maintain transaction state.
-
- Translation of messages can only occur if the agent recognizes the
- application of a particular request; therefore, translation agents
- MUST only advertise their locally supported applications.
-
- +------+ ---------> +------+ ---------> +------+
- | | RADIUS Request | | Diameter Request | |
- | NAS | | TLA | | HMS |
- | | RADIUS Answer | | Diameter Answer | |
- +------+ &lt;--------- +------+ &lt;--------- +------+
- example.net example.net example.com
-
- Figure 4: Translation of RADIUS to Diameter
-</pre>
-
-&nada;
-
-<pre>
-
-
-
-
-Fajardo, et al. Standards Track [Page 32]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-2.9. Diameter Path Authorization
-
- As noted in Section 2.2, Diameter provides transmission-level
- security for each connection using TLS/TCP and DTLS/SCTP. Therefore,
- each connection can be authenticated and can be replay and integrity
- protected.
-
- In addition to authenticating each connection, the entire session
- MUST also be authorized. Before initiating a connection, a Diameter
- peer MUST check that its peers are authorized to act in their roles.
- For example, a Diameter peer may be authentic, but that does not mean
- that it is authorized to act as a Diameter server advertising a set
- of Diameter applications.
-
- Prior to bringing up a connection, authorization checks are performed
- at each connection along the path. Diameter capabilities negotiation
- (CER/CEA) also MUST be carried out, in order to determine what
- Diameter applications are supported by each peer. Diameter sessions
- MUST be routed only through authorized nodes that have advertised
- support for the Diameter application required by the session.
-
- As noted in Section 6.1.9, a relay or proxy agent MUST append a
- Route-Record AVP to all requests forwarded. The AVP contains the
- identity of the peer from which the request was received.
-
- The home Diameter server, prior to authorizing a session, MUST check
- the Route-Record AVPs to make sure that the route traversed by the
- request is acceptable. For example, administrators within the home
- realm may not wish to honor requests that have been routed through an
- untrusted realm. By authorizing a request, the home Diameter server
- is implicitly indicating its willingness to engage in the business
- transaction as specified by any contractual relationship between the
- server and the previous hop. A DIAMETER_AUTHORIZATION_REJECTED error
- message (see Section 7.1.5) is sent if the route traversed by the
- request is unacceptable.
-
- A home realm may also wish to check that each accounting request
- message corresponds to a Diameter response authorizing the session.
- Accounting requests without corresponding authorization responses
- SHOULD be subjected to further scrutiny, as should accounting
- requests indicating a difference between the requested and provided
- service.
-
- Forwarding of an authorization response is considered evidence of a
- willingness to take on financial risk relative to the session. A
- local realm may wish to limit this exposure, for example, by
- establishing credit limits for intermediate realms and refusing to
- accept responses that would violate those limits. By issuing an
-
-
-
-Fajardo, et al. Standards Track [Page 33]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- accounting request corresponding to the authorization response, the
- local realm implicitly indicates its agreement to provide the service
- indicated in the authorization response. If the service cannot be
- provided by the local realm, then a DIAMETER_UNABLE_TO_COMPLY error
- message MUST be sent within the accounting request; a Diameter client
- receiving an authorization response for a service that it cannot
- perform MUST NOT substitute an alternate service and then send
- accounting requests for the alternate service instead.
-</pre>
-
-&nada;
-
-<pre>
-
-3. Diameter Header
-
- A summary of the Diameter header format is shown below. The fields
- are transmitted in network byte order.
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Version | Message Length |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Command Flags | Command Code |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Application-ID |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Hop-by-Hop Identifier |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | End-to-End Identifier |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | AVPs ...
- +-+-+-+-+-+-+-+-+-+-+-+-+-
-</pre>
-
-<p>
-The Diameter Header is represented by the <c>diameter_header</c>
-record defined in <c>diameter.hrl</c>.
-The <c>diameter_packet</c> record contains a <c>header</c> field whose
-value will be a decoded <c>#diameter_header{}</c> for incoming
-messages passed to &app_handle_request; and &app_handle_answer;
-callbacks.
-In the case of outgoing messages, diameter and the relevant
-dictionary populate the Diameter Header appropriately, although
-&app_prepare_request; and &app_handle_request; callbacks can modify
-header values.
-(Which can be useful in test.)</p>
-
-<pre>
-
- Version
-
- This Version field MUST be set to 1 to indicate Diameter Version
- 1.
-
- Message Length
-
- The Message Length field is three octets and indicates the length
- of the Diameter message including the header fields and the padded
- AVPs. Thus, the Message Length field is always a multiple of 4.
-
- Command Flags
-
- The Command Flags field is eight bits. The following bits are
- assigned:
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 34]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- 0 1 2 3 4 5 6 7
- +-+-+-+-+-+-+-+-+
- |R P E T r r r r|
- +-+-+-+-+-+-+-+-+
-
- R(equest)
-
- If set, the message is a request. If cleared, the message is
- an answer.
-
- P(roxiable)
-
- If set, the message MAY be proxied, relayed, or redirected. If
- cleared, the message MUST be locally processed.
-
- E(rror)
-
- If set, the message contains a protocol error, and the message
- will not conform to the CCF described for this command.
- Messages with the 'E' bit set are commonly referred to as error
- messages. This bit MUST NOT be set in request messages (see
- Section 7.2).
-
- T(Potentially retransmitted message)
-
- This flag is set after a link failover procedure, to aid the
- removal of duplicate requests. It is set when resending
- requests not yet acknowledged, as an indication of a possible
- duplicate due to a link failure. This bit MUST be cleared when
- sending a request for the first time; otherwise, the sender
- MUST set this flag. Diameter agents only need to be concerned
- about the number of requests they send based on a single
- received request; retransmissions by other entities need not be
- tracked. Diameter agents that receive a request with the T
- flag set, MUST keep the T flag set in the forwarded request.
- This flag MUST NOT be set if an error answer message (e.g., a
- protocol error) has been received for the earlier message. It
- can be set only in cases where no answer has been received from
- the server for a request, and the request has been sent again.
- This flag MUST NOT be set in answer messages.
-
- r(eserved)
-
- These flag bits are reserved for future use; they MUST be set
- to zero and ignored by the receiver.
-</pre>
-
-<p>
-Reserved bits are set to 0 in outgoing messages.</p>
-
-<pre>
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 35]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Command Code
-
- The Command Code field is three octets and is used in order to
- communicate the command associated with the message. The 24-bit
- address space is managed by IANA (see Section 3.1). Command Code
- values 16,777,214 and 16,777,215 (hexadecimal values FFFFFE-
- FFFFFF) are reserved for experimental use (see Section 11.2).
-
- Application-ID
-
- Application-ID is four octets and is used to identify for which
- application the message is applicable. The application can be an
- authentication application, an accounting application, or a
- vendor-specific application.
-
- The value of the Application-ID field in the header MUST be the
- same as any relevant Application-Id AVPs contained in the message.
-
- Hop-by-Hop Identifier
-
- The Hop-by-Hop Identifier is an unsigned 32-bit integer field (in
- network byte order) that aids in matching requests and replies.
- The sender MUST ensure that the Hop-by-Hop Identifier in a request
- is unique on a given connection at any given time, and it MAY
- attempt to ensure that the number is unique across reboots. The
- sender of an answer message MUST ensure that the Hop-by-Hop
- Identifier field contains the same value that was found in the
- corresponding request. The Hop-by-Hop Identifier is normally a
- monotonically increasing number, whose start value was randomly
- generated. An answer message that is received with an unknown
- Hop-by-Hop Identifier MUST be discarded.
-
- End-to-End Identifier
-
- The End-to-End Identifier is an unsigned 32-bit integer field (in
- network byte order) that is used to detect duplicate messages.
- Upon reboot, implementations MAY set the high order 12 bits to
- contain the low order 12 bits of current time, and the low order
- 20 bits to a random value. Senders of request messages MUST
- insert a unique identifier on each message. The identifier MUST
- remain locally unique for a period of at least 4 minutes, even
- across reboots. The originator of an answer message MUST ensure
- that the End-to-End Identifier field contains the same value that
- was found in the corresponding request. The End-to-End Identifier
- MUST NOT be modified by Diameter agents of any kind. The
- combination of the Origin-Host AVP (Section 6.3) and this field is
- used to detect duplicates. Duplicate requests SHOULD cause the
- same answer to be transmitted (modulo the Hop-by-Hop Identifier
-
-
-
-Fajardo, et al. Standards Track [Page 36]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- field and any routing AVPs that may be present), and they MUST NOT
- affect any state that was set when the original request was
- processed. Duplicate answer messages that are to be locally
- consumed (see Section 6.2) SHOULD be silently discarded.
-
- AVPs
-
- AVPs are a method of encapsulating information relevant to the
- Diameter message. See Section 4 for more information on AVPs.
-</pre>
-
-&nada;
-
-<pre>
-
-3.1. Command Codes
-
- Each command Request/Answer pair is assigned a Command Code, and the
- sub-type (i.e., request or answer) is identified via the 'R' bit in
- the Command Flags field of the Diameter header.
-
- Every Diameter message MUST contain a Command Code in its header's
- Command Code field, which is used to determine the action that is to
- be taken for a particular message. The following Command Codes are
- defined in the Diameter base protocol:
-
- Section
- Command Name Abbrev. Code Reference
- --------------------------------------------------------
- Abort-Session-Request ASR 274 8.5.1
- Abort-Session-Answer ASA 274 8.5.2
- Accounting-Request ACR 271 9.7.1
- Accounting-Answer ACA 271 9.7.2
- Capabilities-Exchange- CER 257 5.3.1
- Request
- Capabilities-Exchange- CEA 257 5.3.2
- Answer
- Device-Watchdog-Request DWR 280 5.5.1
- Device-Watchdog-Answer DWA 280 5.5.2
- Disconnect-Peer-Request DPR 282 5.4.1
- Disconnect-Peer-Answer DPA 282 5.4.2
- Re-Auth-Request RAR 258 8.3.1
- Re-Auth-Answer RAA 258 8.3.2
- Session-Termination- STR 275 8.4.1
- Request
- Session-Termination- STA 275 8.4.2
- Answer
-</pre>
-
-<p>
-These messages are all defined in diameter's implementation of the
-common dictionary in modules <c>diameter_gen_base_rfc6733</c> and
-<c>diameter_gen_base_rfc3588</c>.
-Corresponding record definitions are found in
-<c>diameter_gen_base_rfc6733.hrl</c> and
-<c>diameter_gen_base_rfc3588.hrl</c>.</p>
-
-<pre>
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 37]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-3.2. Command Code Format Specification
-
- Every Command Code defined MUST include a corresponding Command Code
- Format (CCF) specification, which is used to define the AVPs that
- MUST or MAY be present when sending the message. The following ABNF
- specifies the CCF used in the definition:
-</pre>
-
-<p>
-The CCF is what is specified in the <c>@messages</c> section of the
-&man_dict; format, except as noted below.</p>
-
-<pre>
-
- command-def = "&lt;" command-name ">" "::=" diameter-message
-</pre>
-
-<p>
-Angle brackets are currently not allowed here.
-This was a change between RFC 3588 and RFC 6733: the former disallowed
-them in the grammar but included them in its own command definitions.</p>
-
-<pre>
-
- command-name = diameter-name
-
- diameter-name = ALPHA *(ALPHA / DIGIT / "-")
-
- diameter-message = header *fixed *required *optional
-
- header = "&lt;Diameter-Header:" command-id
- [r-bit] [p-bit] [e-bit] [application-id]">"
-
- application-id = 1*DIGIT
-
- command-id = 1*DIGIT
- ; The Command Code assigned to the command.
-
- r-bit = ", REQ"
- ; If present, the 'R' bit in the Command
- ; Flags is set, indicating that the message
- ; is a request as opposed to an answer.
-
- p-bit = ", PXY"
- ; If present, the 'P' bit in the Command
- ; Flags is set, indicating that the message
- ; is proxiable.
-
- e-bit = ", ERR"
- ; If present, the 'E' bit in the Command
- ; Flags is set, indicating that the answer
- ; message contains a Result-Code AVP in
- ; the "protocol error" class.
-
- fixed = [qual] "&lt;" avp-spec ">"
- ; Defines the fixed position of an AVP.
-
- required = [qual] "{" avp-spec "}"
- ; The AVP MUST be present and can appear
- ; anywhere in the message.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 38]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- optional = [qual] "[" avp-name "]"
- ; The avp-name in the 'optional' rule cannot
- ; evaluate to any AVP Name that is included
- ; in a fixed or required rule. The AVP can
- ; appear anywhere in the message.
- ;
- ; NOTE: "[" and "]" have a slightly different
- ; meaning than in ABNF. These braces
- ; cannot be used to express optional fixed rules
- ; (such as an optional ICV at the end). To do
- ; this, the convention is '0*1fixed'.
-
- qual = [min] "*" [max]
- ; See ABNF conventions, RFC 5234, Section 4.
- ; The absence of any qualifier depends on
- ; whether it precedes a fixed, required, or
- ; optional rule. If a fixed or required rule has
- ; no qualifier, then exactly one such AVP MUST
- ; be present. If an optional rule has no
- ; qualifier, then 0 or 1 such AVP may be
- ; present. If an optional rule has a qualifier,
- ; then the value of min MUST be 0 if present.
-
- min = 1*DIGIT
- ; The minimum number of times the element may
- ; be present. If absent, the default value is 0
- ; for fixed and optional rules and 1 for
- ; required rules. The value MUST be at least 1
- ; for required rules.
-
- max = 1*DIGIT
- ; The maximum number of times the element may
- ; be present. If absent, the default value is
- ; infinity. A value of 0 implies the AVP MUST
- ; NOT be present.
-
- avp-spec = diameter-name
- ; The avp-spec has to be an AVP Name, defined
- ; in the base or extended Diameter
- ; specifications.
-
- avp-name = avp-spec / "AVP"
- ; The string "AVP" stands for *any* arbitrary AVP
- ; Name, not otherwise listed in that Command Code
- ; definition. The inclusion of this string
- ; is recommended for all CCFs to allow for
- ; extensibility.
-
-
-
-
-Fajardo, et al. Standards Track [Page 39]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The following is a definition of a fictitious Command Code:
-
- Example-Request ::= &lt; Diameter Header: 9999999, REQ, PXY >
- { User-Name }
- 1* { Origin-Host }
- * [ AVP ]
-</pre>
-
-&nada;
-
-<pre>
-
-3.3. Diameter Command Naming Conventions
-
- Diameter command names typically includes one or more English words
- followed by the verb "Request" or "Answer". Each English word is
- delimited by a hyphen. A three-letter acronym for both the request
- and answer is also normally provided.
-
- An example is a message set used to terminate a session. The command
- name is Session-Terminate-Request and Session-Terminate-Answer, while
- the acronyms are STR and STA, respectively.
-
- Both the request and the answer for a given command share the same
- Command Code. The request is identified by the R(equest) bit in the
- Diameter header set to one (1), to ask that a particular action be
- performed, such as authorizing a user or terminating a session. Once
- the receiver has completed the request, it issues the corresponding
- answer, which includes a result code that communicates one of the
- following:
-
- o The request was successful
-
- o The request failed
-
- o An additional request has to be sent to provide information the
- peer requires prior to returning a successful or failed answer.
-
- o The receiver could not process the request, but provides
- information about a Diameter peer that is able to satisfy the
- request, known as redirect.
-
- Additional information, encoded within AVPs, may also be included in
- answer messages.
-</pre>
-
-<p>
-The &man_dict; format places no requirement on the naming of commands.</p>
-
-<pre>
-
-4. Diameter AVPs
-
- Diameter AVPs carry specific authentication, accounting,
- authorization, and routing information as well as configuration
- details for the request and reply.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 40]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Each AVP of type OctetString MUST be padded to align on a 32-bit
- boundary, while other AVP types align naturally. A number of zero-
- valued bytes are added to the end of the AVP Data field until a word
- boundary is reached. The length of the padding is not reflected in
- the AVP Length field.
-
-4.1. AVP Header
-
- The fields in the AVP header MUST be sent in network byte order. The
- format of the header is:
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | AVP Code |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- |V M P r r r r r| AVP Length |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Vendor-ID (opt) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Data ...
- +-+-+-+-+-+-+-+-+
-
- AVP Code
-
- The AVP Code, combined with the Vendor-Id field, identifies the
- attribute uniquely. AVP numbers 1 through 255 are reserved for
- reuse of RADIUS attributes, without setting the Vendor-Id field.
- AVP numbers 256 and above are used for Diameter, which are
- allocated by IANA (see Section 11.1.1).
-
- AVP Flags
-
- The AVP Flags field informs the receiver how each attribute must
- be handled. New Diameter applications SHOULD NOT define
- additional AVP Flag bits. However, note that new Diameter
- applications MAY define additional bits within the AVP header, and
- an unrecognized bit SHOULD be considered an error. The sender of
- the AVP MUST set 'R' (reserved) bits to 0 and the receiver SHOULD
- ignore all 'R' (reserved) bits. The 'P' bit has been reserved for
- future usage of end-to-end security. At the time of writing,
- there are no end-to-end security mechanisms specified; therefore,
- the 'P' bit SHOULD be set to 0.
-
- The 'M' bit, known as the Mandatory bit, indicates whether the
- receiver of the AVP MUST parse and understand the semantics of the
- AVP including its content. The receiving entity MUST return an
- appropriate error message if it receives an AVP that has the M-bit
-
-
-
-Fajardo, et al. Standards Track [Page 41]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- set but does not understand it. An exception applies when the AVP
- is embedded within a Grouped AVP. See Section 4.4 for details.
- Diameter relay and redirect agents MUST NOT reject messages with
- unrecognized AVPs.
-
- The 'M' bit MUST be set according to the rules defined in the
- application specification that introduces or reuses this AVP.
- Within a given application, the M-bit setting for an AVP is
- defined either for all command types or for each command type.
-
- AVPs with the 'M' bit cleared are informational only; a receiver
- that receives a message with such an AVP that is not supported, or
- whose value is not supported, MAY simply ignore the AVP.
-
- The 'V' bit, known as the Vendor-Specific bit, indicates whether
- the optional Vendor-ID field is present in the AVP header. When
- set, the AVP Code belongs to the specific vendor code address
- space.
-
- AVP Length
-
- The AVP Length field is three octets, and indicates the number of
- octets in this AVP including the AVP Code field, AVP Length field,
- AVP Flags field, Vendor-ID field (if present), and the AVP Data
- field. If a message is received with an invalid attribute length,
- the message MUST be rejected.
-
-4.1.1. Optional Header Elements
-
- The AVP header contains one optional field. This field is only
- present if the respective bit-flag is enabled.
-
- Vendor-ID
-
- The Vendor-ID field is present if the 'V' bit is set in the AVP
- Flags field. The optional four-octet Vendor-ID field contains the
- IANA-assigned "SMI Network Management Private Enterprise Codes"
- [ENTERPRISE] value, encoded in network byte order. Any vendors or
- standardization organizations that are also treated like vendors
- in the IANA-managed "SMI Network Management Private Enterprise
- Codes" space wishing to implement a vendor-specific Diameter AVP
- MUST use their own Vendor-ID along with their privately managed
- AVP address space, guaranteeing that they will not collide with
- any other vendor's vendor-specific AVP(s) or with future IETF
- AVPs.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 42]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- A Vendor-ID value of zero (0) corresponds to the IETF-adopted AVP
- values, as managed by IANA. Since the absence of the Vendor-ID
- field implies that the AVP in question is not vendor specific,
- implementations MUST NOT use the value of zero (0) for the
- Vendor-ID field.
-
-4.2. Basic AVP Data Formats
-
- The Data field is zero or more octets and contains information
- specific to the Attribute. The format and length of the Data field
- is determined by the AVP Code and AVP Length fields. The format of
- the Data field MUST be one of the following base data types or a data
- type derived from the base data types. In the event that a new Basic
- AVP Data Format is needed, a new version of this RFC MUST be created.
-
- OctetString
-
- The data contains arbitrary data of variable length. Unless
- otherwise noted, the AVP Length field MUST be set to at least 8
- (12 if the 'V' bit is enabled). AVP values of this type that are
- not a multiple of 4 octets in length are followed by the necessary
- padding so that the next AVP (if any) will start on a 32-bit
- boundary.
-
- Integer32
-
- 32-bit signed value, in network byte order. The AVP Length field
- MUST be set to 12 (16 if the 'V' bit is enabled).
-
- Integer64
-
- 64-bit signed value, in network byte order. The AVP Length field
- MUST be set to 16 (20 if the 'V' bit is enabled).
-
- Unsigned32
-
- 32-bit unsigned value, in network byte order. The AVP Length
- field MUST be set to 12 (16 if the 'V' bit is enabled).
-
- Unsigned64
-
- 64-bit unsigned value, in network byte order. The AVP Length
- field MUST be set to 16 (20 if the 'V' bit is enabled).
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 43]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Float32
-
- This represents floating point values of single precision as
- described by [FLOATPOINT]. The 32-bit value is transmitted in
- network byte order. The AVP Length field MUST be set to 12 (16 if
- the 'V' bit is enabled).
-
- Float64
-
- This represents floating point values of double precision as
- described by [FLOATPOINT]. The 64-bit value is transmitted in
- network byte order. The AVP Length field MUST be set to 16 (20 if
- the 'V' bit is enabled).
-
- Grouped
-
- The Data field is specified as a sequence of AVPs. These AVPs are
- concatenated -- including their headers and padding -- in the
- order in which they are specified and the result encapsulated in
- the Data field. The AVP Length field is set to 8 (12 if the 'V'
- bit is enabled) plus the total length of all included AVPs,
- including their headers and padding. Thus, the AVP Length field
- of an AVP of type Grouped is always a multiple of 4.
-
-4.3. Derived AVP Data Formats
-
- In addition to using the Basic AVP Data Formats, applications may
- define data formats derived from the Basic AVP Data Formats. An
- application that defines new Derived AVP Data Formats MUST include
- them in a section titled "Derived AVP Data Formats", using the same
- format as the definitions below. Each new definition MUST be either
- defined or listed with a reference to the RFC that defines the
- format.
-
-4.3.1. Common Derived AVP Data Formats
-
- The following are commonly used Derived AVP Data Formats.
-
- Address
-
- The Address format is derived from the OctetString Basic AVP
- Format. It is a discriminated union representing, for example, a
- 32-bit (IPv4) [RFC0791] or 128-bit (IPv6) [RFC4291] address, most
- significant octet first. The first two octets of the Address AVP
- represent the AddressType, which contains an Address Family,
- defined in [IANAADFAM]. The AddressType is used to discriminate
- the content and format of the remaining octets.
-
-
-
-
-Fajardo, et al. Standards Track [Page 44]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Time
-
- The Time format is derived from the OctetString Basic AVP Format.
- The string MUST contain four octets, in the same format as the
- first four bytes are in the NTP timestamp format. The NTP
- timestamp format is defined in Section 3 of [RFC5905].
-
- This represents the number of seconds since 0h on 1 January 1900
- with respect to the Coordinated Universal Time (UTC).
-
- On 6h 28m 16s UTC, 7 February 2036, the time value will overflow.
- Simple Network Time Protocol (SNTP) [RFC5905] describes a
- procedure to extend the time to 2104. This procedure MUST be
- supported by all Diameter nodes.
-
- UTF8String
-
- The UTF8String format is derived from the OctetString Basic AVP
- Format. This is a human-readable string represented using the
- ISO/IEC IS 10646-1 character set, encoded as an OctetString using
- the UTF-8 transformation format [RFC3629].
-
- Since additional code points are added by amendments to the 10646
- standard from time to time, implementations MUST be prepared to
- encounter any code point from 0x00000001 to 0x7fffffff. Byte
- sequences that do not correspond to the valid encoding of a code
- point into UTF-8 charset or are outside this range are prohibited.
-
- The use of control codes SHOULD be avoided. When it is necessary
- to represent a new line, the control code sequence CR LF SHOULD be
- used.
-
- The use of leading or trailing white space SHOULD be avoided.
-
- For code points not directly supported by user interface hardware
- or software, an alternative means of entry and display, such as
- hexadecimal, MAY be provided.
-
- For information encoded in 7-bit US-ASCII, the UTF-8 charset is
- identical to the US-ASCII charset.
-
- UTF-8 may require multiple bytes to represent a single character /
- code point; thus, the length of a UTF8String in octets may be
- different from the number of characters encoded.
-
- Note that the AVP Length field of an UTF8String is measured in
- octets not characters.
-
-
-
-
-Fajardo, et al. Standards Track [Page 45]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- DiameterIdentity
-
- The DiameterIdentity format is derived from the OctetString Basic
- AVP Format.
-
- DiameterIdentity = FQDN/Realm
-
- The DiameterIdentity value is used to uniquely identify either:
-
- * A Diameter node for purposes of duplicate connection and
- routing loop detection.
-
- * A Realm to determine whether messages can be satisfied locally
- or whether they must be routed or redirected.
-
- When a DiameterIdentity value is used to identify a Diameter node,
- the contents of the string MUST be the Fully Qualified Domain Name
- (FQDN) of the Diameter node. If multiple Diameter nodes run on
- the same host, each Diameter node MUST be assigned a unique
- DiameterIdentity. If a Diameter node can be identified by several
- FQDNs, a single FQDN should be picked at startup and used as the
- only DiameterIdentity for that node, whatever the connection on
- which it is sent. In this document, note that DiameterIdentity is
- in ASCII form in order to be compatible with existing DNS
- infrastructure. See Appendix D for interactions between the
- Diameter protocol and Internationalized Domain Names (IDNs).
-
- DiameterURI
-
- The DiameterURI MUST follow the Uniform Resource Identifiers (RFC
- 3986) syntax [RFC3986] rules specified below:
-
- "aaa://" FQDN [ port ] [ transport ] [ protocol ]
-
- ; No transport security
-
- "aaas://" FQDN [ port ] [ transport ] [ protocol ]
-
- ; Transport security used
-
- FQDN = &lt; Fully Qualified Domain Name >
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 46]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- port = ":" 1*DIGIT
-
- ; One of the ports used to listen for
- ; incoming connections.
- ; If absent, the default Diameter port
- ; (3868) is assumed if no transport
- ; security is used and port 5658 when
- ; transport security (TLS/TCP and DTLS/SCTP)
- ; is used.
-
- transport = ";transport=" transport-protocol
-
- ; One of the transports used to listen
- ; for incoming connections. If absent,
- ; the default protocol is assumed to be TCP.
- ; UDP MUST NOT be used when the aaa-protocol
- ; field is set to diameter.
-
- transport-protocol = ( "tcp" / "sctp" / "udp" )
-
- protocol = ";protocol=" aaa-protocol
-
- ; If absent, the default AAA protocol
- ; is Diameter.
-
- aaa-protocol = ( "diameter" / "radius" / "tacacs+" )
-
- The following are examples of valid Diameter host identities:
-
- aaa://host.example.com;transport=tcp
- aaa://host.example.com:6666;transport=tcp
- aaa://host.example.com;protocol=diameter
- aaa://host.example.com:6666;protocol=diameter
- aaa://host.example.com:6666;transport=tcp;protocol=diameter
- aaa://host.example.com:1813;transport=udp;protocol=radius
-
- Enumerated
-
- The Enumerated format is derived from the Integer32 Basic AVP
- Format. The definition contains a list of valid values and their
- interpretation and is described in the Diameter application
- introducing the AVP.
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 47]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- IPFilterRule
-
- The IPFilterRule format is derived from the OctetString Basic AVP
- Format and uses the ASCII charset. The rule syntax is a modified
- subset of ipfw(8) from FreeBSD. Packets may be filtered based on
- the following information that is associated with it:
-
- Direction (in or out)
- Source and destination IP address (possibly masked)
- Protocol
- Source and destination port (lists or ranges)
- TCP flags
- IP fragment flag
- IP options
- ICMP types
-
- Rules for the appropriate direction are evaluated in order, with the
- first matched rule terminating the evaluation. Each packet is
- evaluated once. If no rule matches, the packet is dropped if the
- last rule evaluated was a permit, and passed if the last rule was a
- deny.
-
- IPFilterRule filters MUST follow the format:
-
- action dir proto from src to dst [options]
-
- action permit - Allow packets that match the rule.
- deny - Drop packets that match the rule.
-
- dir "in" is from the terminal, "out" is to the
- terminal.
-
- proto An IP protocol specified by number. The "ip"
- keyword means any protocol will match.
-
- src and dst &lt;address/mask> [ports]
-
- The &lt;address/mask> may be specified as:
- ipno An IPv4 or IPv6 number in dotted-
- quad or canonical IPv6 form. Only
- this exact IP number will match the
- rule.
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 48]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- ipno/bits An IP number as above with a mask
- width of the form 192.0.2.10/24. In
- this case, all IP numbers from
- 192.0.2.0 to 192.0.2.255 will match.
- The bit width MUST be valid for the
- IP version, and the IP number MUST
- NOT have bits set beyond the mask.
- For a match to occur, the same IP
- version must be present in the
- packet that was used in describing
- the IP address. To test for a
- particular IP version, the bits part
- can be set to zero. The keyword
- "any" is 0.0.0.0/0 or the IPv6
- equivalent. The keyword "assigned"
- is the address or set of addresses
- assigned to the terminal. For IPv4,
- a typical first rule is often "deny
- in ip! assigned".
-
- The sense of the match can be inverted by
- preceding an address with the not modifier (!),
- causing all other addresses to be matched
- instead. This does not affect the selection of
- port numbers.
-
- With the TCP, UDP, and SCTP protocols, optional
- ports may be specified as:
-
- {port/port-port}[,ports[,...]]
-
- The '-' notation specifies a range of ports
- (including boundaries).
-
- Fragmented packets that have a non-zero offset
- (i.e., not the first fragment) will never match
- a rule that has one or more port
- specifications. See the frag option for
- details on matching fragmented packets.
-
- options:
- frag Match if the packet is a fragment and this is not
- the first fragment of the datagram. frag may not
- be used in conjunction with either tcpflags or
- TCP/UDP port specifications.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 49]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- ipoptions spec
- Match if the IP header contains the comma-separated
- list of options specified in spec. The
- supported IP options are:
-
- ssrr (strict source route), lsrr (loose source
- route), rr (record packet route), and ts
- (timestamp). The absence of a particular option
- may be denoted with a '!'.
-
- tcpoptions spec
- Match if the TCP header contains the comma-separated
- list of options specified in spec. The
- supported TCP options are:
-
- mss (maximum segment size), window (tcp window
- advertisement), sack (selective ack), ts (rfc1323
- timestamp), and cc (rfc1644 t/tcp connection
- count). The absence of a particular option may
- be denoted with a '!'.
-
- established
- TCP packets only. Match packets that have the RST
- or ACK bits set.
-
- setup TCP packets only. Match packets that have the SYN
- bit set but no ACK bit.
-
-
- tcpflags spec
- TCP packets only. Match if the TCP header
- contains the comma-separated list of flags
- specified in spec. The supported TCP flags are:
-
- fin, syn, rst, psh, ack, and urg. The absence of a
- particular flag may be denoted with a '!'. A rule
- that contains a tcpflags specification can never
- match a fragmented packet that has a non-zero
- offset. See the frag option for details on
- matching fragmented packets.
-
- icmptypes types
- ICMP packets only. Match if the ICMP type is in
- the list types. The list may be specified as any
- combination of ranges or individual types
- separated by commas. Both the numeric values and
- the symbolic values listed below can be used. The
- supported ICMP types are:
-
-
-
-Fajardo, et al. Standards Track [Page 50]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- echo reply (0), destination unreachable (3),
- source quench (4), redirect (5), echo request
- (8), router advertisement (9), router
- solicitation (10), time-to-live exceeded (11), IP
- header bad (12), timestamp request (13),
- timestamp reply (14), information request (15),
- information reply (16), address mask request (17),
- and address mask reply (18).
-
- There is one kind of packet that the access device MUST always
- discard, that is an IP fragment with a fragment offset of one. This
- is a valid packet, but it only has one use, to try to circumvent
- firewalls.
-
- An access device that is unable to interpret or apply a deny rule
- MUST terminate the session. An access device that is unable to
- interpret or apply a permit rule MAY apply a more restrictive rule.
- An access device MAY apply deny rules of its own before the supplied
- rules, for example to protect the access device owner's
- infrastructure.
-
-4.4. Grouped AVP Values
-
- The Diameter protocol allows AVP values of type 'Grouped'. This
- implies that the Data field is actually a sequence of AVPs. It is
- possible to include an AVP with a Grouped type within a Grouped type,
- that is, to nest them. AVPs within an AVP of type Grouped have the
- same padding requirements as non-Grouped AVPs, as defined in
- Section 4.4.
-
- The AVP Code numbering space of all AVPs included in a Grouped AVP is
- the same as for non-Grouped AVPs. Receivers of a Grouped AVP that
- does not have the 'M' (mandatory) bit set and one or more of the
- encapsulated AVPs within the group has the 'M' (mandatory) bit set
- MAY simply be ignored if the Grouped AVP itself is unrecognized. The
- rule applies even if the encapsulated AVP with its 'M' (mandatory)
- bit set is further encapsulated within other sub-groups, i.e., other
- Grouped AVPs embedded within the Grouped AVP.
-
- Every Grouped AVP definition MUST include a corresponding grammar,
- using ABNF [RFC5234] (with modifications), as defined below.
-
- grouped-avp-def = "&lt;" name ">" "::=" avp
-
- name-fmt = ALPHA *(ALPHA / DIGIT / "-")
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 51]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- name = name-fmt
- ; The name has to be the name of an AVP,
- ; defined in the base or extended Diameter
- ; specifications.
-
- avp = header *fixed *required *optional
-
- header = "&lt;" "AVP-Header:" avpcode [vendor] ">"
-
- avpcode = 1*DIGIT
- ; The AVP Code assigned to the Grouped AVP.
-
- vendor = 1*DIGIT
- ; The Vendor-ID assigned to the Grouped AVP.
- ; If absent, the default value of zero is
- ; used.
-
-4.4.1. Example AVP with a Grouped Data Type
-
- The Example-AVP (AVP Code 999999) is of type Grouped and is used to
- clarify how Grouped AVP values work. The Grouped Data field has the
- following CCF grammar:
-
- Example-AVP ::= &lt; AVP Header: 999999 >
- { Origin-Host }
- 1*{ Session-Id }
- *[ AVP ]
-
- An Example-AVP with Grouped Data follows.
-
- The Origin-Host AVP (Section 6.3) is required. In this case:
-
- Origin-Host = "example.com".
-
- One or more Session-Ids must follow. Here there are two:
-
- Session-Id =
- "grump.example.com:33041;23432;893;0AF3B81"
-
- Session-Id =
- "grump.example.com:33054;23561;2358;0AF3B82"
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 52]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- optional AVPs included are
-
- Recovery-Policy = &lt;binary>
- 2163bc1d0ad82371f6bc09484133c3f09ad74a0dd5346d54195a7cf0b35
- 2cabc881839a4fdcfbc1769e2677a4c1fb499284c5f70b48f58503a45c5
- c2d6943f82d5930f2b7c1da640f476f0e9c9572a50db8ea6e51e1c2c7bd
- f8bb43dc995144b8dbe297ac739493946803e1cee3e15d9b765008a1b2a
- cf4ac777c80041d72c01e691cf751dbf86e85f509f3988e5875dc905119
- 26841f00f0e29a6d1ddc1a842289d440268681e052b30fb638045f7779c
- 1d873c784f054f688f5001559ecff64865ef975f3e60d2fd7966b8c7f92
-
- Futuristic-Acct-Record = &lt;binary>
- fe19da5802acd98b07a5b86cb4d5d03f0314ab9ef1ad0b67111ff3b90a0
- 57fe29620bf3585fd2dd9fcc38ce62f6cc208c6163c008f4258d1bc88b8
- 17694a74ccad3ec69269461b14b2e7a4c111fb239e33714da207983f58c
- 41d018d56fe938f3cbf089aac12a912a2f0d1923a9390e5f789cb2e5067
- d3427475e49968f841
-
- The data for the optional AVPs is represented in hexadecimal form
- since the format of these AVPs is not known at the time of definition
- of the Example-AVP group nor (likely) at the time when the example
- instance of this AVP is interpreted -- except by Diameter
- implementations that support the same set of AVPs. The encoding
- example illustrates how padding is used and how length fields are
- calculated. Also, note that AVPs may be present in the Grouped AVP
- value that the receiver cannot interpret (here, the Recover-Policy
- and Futuristic-Acct-Record AVPs). The length of the Example-AVP is
- the sum of all the length of the member AVPs, including their
- padding, plus the Example-AVP header size.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 53]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- This AVP would be encoded as follows:
-
- 0 1 2 3 4 5 6 7
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 0 | Example AVP Header (AVP Code = 999999), Length = 496 |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 8 | Origin-Host AVP Header (AVP Code = 264), Length = 19 |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 16 | 'e' | 'x' | 'a' | 'm' | 'p' | 'l' | 'e' | '.' |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 24 | 'c' | 'o' | 'm' |Padding| Session-Id AVP Header |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 32 | (AVP Code = 263), Length = 49 | 'g' | 'r' | 'u' | 'm' |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- . . .
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 72 | 'F' | '3' | 'B' | '8' | '1' |Padding|Padding|Padding|
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 80 | Session-Id AVP Header (AVP Code = 263), Length = 50 |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 88 | 'g' | 'r' | 'u' | 'm' | 'p' | '.' | 'e' | 'x' |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- . . .
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 120| '5' | '8' | ';' | '0' | 'A' | 'F' | '3' | 'B' |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 128| '8' | '2' |Padding|Padding| Recovery-Policy Header (AVP |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 136| Code = 8341), Length = 223 | 0x21 | 0x63 | 0xbc | 0x1d |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 144| 0x0a | 0xd8 | 0x23 | 0x71 | 0xf6 | 0xbc | 0x09 | 0x48 |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- . . .
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 352| 0x8c | 0x7f | 0x92 |Padding| Futuristic-Acct-Record Header |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 328|(AVP Code = 15930),Length = 137| 0xfe | 0x19 | 0xda | 0x58 |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 336| 0x02 | 0xac | 0xd9 | 0x8b | 0x07 | 0xa5 | 0xb8 | 0xc6 |
- +-------+-------+-------+-------+-------+-------+-------+-------+
- . . .
- +-------+-------+-------+-------+-------+-------+-------+-------+
- 488| 0xe4 | 0x99 | 0x68 | 0xf8 | 0x41 |Padding|Padding|Padding|
- +-------+-------+-------+-------+-------+-------+-------+-------+
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 54]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-4.5. Diameter Base Protocol AVPs
-
- The following table describes the Diameter AVPs defined in the base
- protocol, their AVP Code values, types, and possible flag values.
-
- Due to space constraints, the short form DiamIdent is used to
- represent DiameterIdentity.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 55]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- +----------+
- | AVP Flag |
- | rules |
- |----+-----|
- AVP Section | |MUST |
- Attribute Name Code Defined Data Type |MUST| NOT |
- -----------------------------------------|----+-----|
- Acct- 85 9.8.2 Unsigned32 | M | V |
- Interim-Interval | | |
- Accounting- 483 9.8.7 Enumerated | M | V |
- Realtime-Required | | |
- Acct- 50 9.8.5 UTF8String | M | V |
- Multi-Session-Id | | |
- Accounting- 485 9.8.3 Unsigned32 | M | V |
- Record-Number | | |
- Accounting- 480 9.8.1 Enumerated | M | V |
- Record-Type | | |
- Acct- 44 9.8.4 OctetString| M | V |
- Session-Id | | |
- Accounting- 287 9.8.6 Unsigned64 | M | V |
- Sub-Session-Id | | |
- Acct- 259 6.9 Unsigned32 | M | V |
- Application-Id | | |
- Auth- 258 6.8 Unsigned32 | M | V |
- Application-Id | | |
- Auth-Request- 274 8.7 Enumerated | M | V |
- Type | | |
- Authorization- 291 8.9 Unsigned32 | M | V |
- Lifetime | | |
- Auth-Grace- 276 8.10 Unsigned32 | M | V |
- Period | | |
- Auth-Session- 277 8.11 Enumerated | M | V |
- State | | |
- Re-Auth-Request- 285 8.12 Enumerated | M | V |
- Type | | |
- Class 25 8.20 OctetString| M | V |
- Destination-Host 293 6.5 DiamIdent | M | V |
- Destination- 283 6.6 DiamIdent | M | V |
- Realm | | |
- Disconnect-Cause 273 5.4.3 Enumerated | M | V |
- Error-Message 281 7.3 UTF8String | | V,M |
- Error-Reporting- 294 7.4 DiamIdent | | V,M |
- Host | | |
- Event-Timestamp 55 8.21 Time | M | V |
- Experimental- 297 7.6 Grouped | M | V |
- Result | | |
- -----------------------------------------|----+-----|
-
-
-
-
-Fajardo, et al. Standards Track [Page 56]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- +----------+
- | AVP Flag |
- | rules |
- |----+-----|
- AVP Section | |MUST |
- Attribute Name Code Defined Data Type |MUST| NOT |
- -----------------------------------------|----+-----|
- Experimental- 298 7.7 Unsigned32 | M | V |
- Result-Code | | |
- Failed-AVP 279 7.5 Grouped | M | V |
- Firmware- 267 5.3.4 Unsigned32 | | V,M |
- Revision | | |
- Host-IP-Address 257 5.3.5 Address | M | V |
- Inband-Security | M | V |
- -Id 299 6.10 Unsigned32 | | |
- Multi-Round- 272 8.19 Unsigned32 | M | V |
- Time-Out | | |
- Origin-Host 264 6.3 DiamIdent | M | V |
- Origin-Realm 296 6.4 DiamIdent | M | V |
- Origin-State-Id 278 8.16 Unsigned32 | M | V |
- Product-Name 269 5.3.7 UTF8String | | V,M |
- Proxy-Host 280 6.7.3 DiamIdent | M | V |
- Proxy-Info 284 6.7.2 Grouped | M | V |
- Proxy-State 33 6.7.4 OctetString| M | V |
- Redirect-Host 292 6.12 DiamURI | M | V |
- Redirect-Host- 261 6.13 Enumerated | M | V |
- Usage | | |
- Redirect-Max- 262 6.14 Unsigned32 | M | V |
- Cache-Time | | |
- Result-Code 268 7.1 Unsigned32 | M | V |
- Route-Record 282 6.7.1 DiamIdent | M | V |
- Session-Id 263 8.8 UTF8String | M | V |
- Session-Timeout 27 8.13 Unsigned32 | M | V |
- Session-Binding 270 8.17 Unsigned32 | M | V |
- Session-Server- 271 8.18 Enumerated | M | V |
- Failover | | |
- Supported- 265 5.3.6 Unsigned32 | M | V |
- Vendor-Id | | |
- Termination- 295 8.15 Enumerated | M | V |
- Cause | | |
- User-Name 1 8.14 UTF8String | M | V |
- Vendor-Id 266 5.3.3 Unsigned32 | M | V |
- Vendor-Specific- 260 6.11 Grouped | M | V |
- Application-Id | | |
- -----------------------------------------|----+-----|
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 57]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-5. Diameter Peers
-
- This section describes how Diameter nodes establish connections and
- communicate with peers.
-
-5.1. Peer Connections
-
- Connections between diameter peers are established using their valid
- DiameterIdentity. A Diameter node initiating a connection to a peer
- MUST know the peer's DiameterIdentity. Methods for discovering a
- Diameter peer can be found in Section 5.2.
-
- Although a Diameter node may have many possible peers with which it
- is able to communicate, it may not be economical to have an
- established connection to all of them. At a minimum, a Diameter node
- SHOULD have an established connection with two peers per realm, known
- as the primary and secondary peers. Of course, a node MAY have
- additional connections, if it is deemed necessary. Typically, all
- messages for a realm are sent to the primary peer but, in the event
- that failover procedures are invoked, any pending requests are sent
- to the secondary peer. However, implementations are free to load
- balance requests between a set of peers.
-
- Note that a given peer MAY act as a primary for a given realm while
- acting as a secondary for another realm.
-
- When a peer is deemed suspect, which could occur for various reasons,
- including not receiving a DWA within an allotted time frame, no new
- requests should be forwarded to the peer, but failover procedures are
- invoked. When an active peer is moved to this mode, additional
- connections SHOULD be established to ensure that the necessary number
- of active connections exists.
-
- There are two ways that a peer is removed from the suspect peer list:
-
- 1. The peer is no longer reachable, causing the transport connection
- to be shut down. The peer is moved to the closed state.
-
- 2. Three watchdog messages are exchanged with accepted round-trip
- times, and the connection to the peer is considered stabilized.
-
- In the event the peer being removed is either the primary or
- secondary, an alternate peer SHOULD replace the deleted peer and
- assume the role of either primary or secondary.
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 58]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-5.2. Diameter Peer Discovery
-
- Allowing for dynamic Diameter agent discovery makes possible simpler
- and more robust deployment of Diameter services. In order to promote
- interoperable implementations of Diameter peer discovery, the
- following mechanisms (manual configuration and DNS) are described.
- These are based on existing IETF standards. Both mechanisms MUST be
- supported by all Diameter implementations; either MAY be used.
-
- There are two cases where Diameter peer discovery may be performed.
- The first is when a Diameter client needs to discover a first-hop
- Diameter agent. The second case is when a Diameter agent needs to
- discover another agent for further handling of a Diameter operation.
- In both cases, the following 'search order' is recommended:
-
- 1. The Diameter implementation consults its list of statically
- (manually) configured Diameter agent locations. These will be
- used if they exist and respond.
-
- 2. The Diameter implementation performs a NAPTR query for a server
- in a particular realm. The Diameter implementation has to know,
- in advance, in which realm to look for a Diameter agent. This
- could be deduced, for example, from the 'realm' in an NAI on
- which a Diameter implementation needed to perform a Diameter
- operation.
-
- The NAPTR usage in Diameter follows the S-NAPTR DDDS application
- [RFC3958] in which the SERVICE field includes tags for the
- desired application and supported application protocol. The
- application service tag for a Diameter application is 'aaa' and
- the supported application protocol tags are 'diameter.tcp',
- 'diameter.sctp', 'diameter.dtls', or 'diameter.tls.tcp'
- [RFC6408].
-
- The client can follow the resolution process defined by the
- S-NAPTR DDDS [RFC3958] application to find a matching SRV, A, or
- AAAA record of a suitable peer. The domain suffixes in the NAPTR
- replacement field SHOULD match the domain of the original query.
- An example can be found in Appendix B.
-
- 3. If no NAPTR records are found, the requester directly queries for
- one of the following SRV records: for Diameter over TCP, use
- "_diameter._tcp.realm"; for Diameter over TLS, use
- "_diameters._tcp.realm"; for Diameter over SCTP, use
- "_diameter._sctp.realm"; for Diameter over DTLS, use
- "_diameters._sctp.realm". If SRV records are found, then the
- requester can perform address record query (A RR's and/or AAAA
-
-
-
-
-Fajardo, et al. Standards Track [Page 59]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- RR's) for the target hostname specified in the SRV records
- following the rules given in [RFC2782]. If no SRV records are
- found, the requester gives up.
-
- If the server is using a site certificate, the domain name in the
- NAPTR query and the domain name in the replacement field MUST both be
- valid based on the site certificate handed out by the server in the
- TLS/TCP and DTLS/SCTP or Internet Key Exchange Protocol (IKE)
- exchange. Similarly, the domain name in the SRV query and the domain
- name in the target in the SRV record MUST both be valid based on the
- same site certificate. Otherwise, an attacker could modify the DNS
- records to contain replacement values in a different domain, and the
- client could not validate whether this was the desired behavior or
- the result of an attack.
-
- Also, the Diameter peer MUST check to make sure that the discovered
- peers are authorized to act in its role. Authentication via IKE or
- TLS/TCP and DTLS/SCTP, or validation of DNS RRs via DNSSEC is not
- sufficient to conclude this. For example, a web server may have
- obtained a valid TLS/TCP and DTLS/SCTP certificate, and secured RRs
- may be included in the DNS, but this does not imply that it is
- authorized to act as a Diameter server.
-
- Authorization can be achieved, for example, by the configuration of a
- Diameter server Certification Authority (CA). The server CA issues a
- certificate to the Diameter server, which includes an Object
- Identifier (OID) to indicate the subject is a Diameter server in the
- Extended Key Usage extension [RFC5280]. This certificate is then
- used during TLS/TCP, DTLS/SCTP, or IKE security negotiation.
- However, note that, at the time of writing, no Diameter server
- Certification Authorities exist.
-
- A dynamically discovered peer causes an entry in the peer table (see
- Section 2.6) to be created. Note that entries created via DNS MUST
- expire (or be refreshed) within the DNS Time to Live (TTL). If a
- peer is discovered outside of the local realm, a routing table entry
- (see Section 2.7) for the peer's realm is created. The routing table
- entry's expiration MUST match the peer's expiration value.
-
-5.3. Capabilities Exchange
-
- When two Diameter peers establish a transport connection, they MUST
- exchange the Capabilities Exchange messages, as specified in the peer
- state machine (see Section 5.6). This message allows the discovery
- of a peer's identity and its capabilities (protocol version number,
- the identifiers of supported Diameter applications, security
- mechanisms, etc.).
-
-
-
-
-Fajardo, et al. Standards Track [Page 60]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The receiver only issues commands to its peers that have advertised
- support for the Diameter application that defines the command. A
- Diameter node MUST cache the supported Application Ids in order to
- ensure that unrecognized commands and/or AVPs are not unnecessarily
- sent to a peer.
-
- A receiver of a Capabilities-Exchange-Request (CER) message that does
- not have any applications in common with the sender MUST return a
- Capabilities-Exchange-Answer (CEA) with the Result-Code AVP set to
- DIAMETER_NO_COMMON_APPLICATION and SHOULD disconnect the transport
- layer connection. Note that receiving a CER or CEA from a peer
- advertising itself as a relay (see Section 2.4) MUST be interpreted
- as having common applications with the peer.
-
- The receiver of the Capabilities-Exchange-Request (CER) MUST
- determine common applications by computing the intersection of its
- own set of supported Application Ids against all of the
- Application-Id AVPs (Auth-Application-Id, Acct-Application-Id, and
- Vendor-Specific-Application-Id) present in the CER. The value of the
- Vendor-Id AVP in the Vendor-Specific-Application-Id MUST NOT be used
- during computation. The sender of the Capabilities-Exchange-Answer
- (CEA) SHOULD include all of its supported applications as a hint to
- the receiver regarding all of its application capabilities.
-
- Diameter implementations SHOULD first attempt to establish a TLS/TCP
- and DTLS/SCTP connection prior to the CER/CEA exchange. This
- protects the capabilities information of both peers. To support
- older Diameter implementations that do not fully conform to this
- document, the transport security MAY still be negotiated via an
- Inband-Security AVP. In this case, the receiver of a Capabilities-
- Exchange-Request (CER) message that does not have any security
- mechanisms in common with the sender MUST return a Capabilities-
- Exchange-Answer (CEA) with the Result-Code AVP set to
- DIAMETER_NO_COMMON_SECURITY and SHOULD disconnect the transport layer
- connection.
-
- CERs received from unknown peers MAY be silently discarded, or a CEA
- MAY be issued with the Result-Code AVP set to DIAMETER_UNKNOWN_PEER.
- In both cases, the transport connection is closed. If the local
- policy permits receiving CERs from unknown hosts, a successful CEA
- MAY be returned. If a CER from an unknown peer is answered with a
- successful CEA, the lifetime of the peer entry is equal to the
- lifetime of the transport connection. In case of a transport
- failure, all the pending transactions destined to the unknown peer
- can be discarded.
-
- The CER and CEA messages MUST NOT be proxied, redirected, or relayed.
-
-
-
-
-Fajardo, et al. Standards Track [Page 61]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Since the CER/CEA messages cannot be proxied, it is still possible
- that an upstream agent will receive a message for which it has no
- available peers to handle the application that corresponds to the
- Command Code. In such instances, the 'E' bit is set in the answer
- message (Section 7) with the Result-Code AVP set to
- DIAMETER_UNABLE_TO_DELIVER to inform the downstream agent to take
- action (e.g., re-routing request to an alternate peer).
-
- With the exception of the Capabilities-Exchange-Request message, a
- message of type Request that includes the Auth-Application-Id or
- Acct-Application-Id AVPs, or a message with an application-specific
- Command Code MAY only be forwarded to a host that has explicitly
- advertised support for the application (or has advertised the Relay
- Application Id).
-
-5.3.1. Capabilities-Exchange-Request
-
- The Capabilities-Exchange-Request (CER), indicated by the Command
- Code set to 257 and the Command Flags' 'R' bit set, is sent to
- exchange local capabilities. Upon detection of a transport failure,
- this message MUST NOT be sent to an alternate peer.
-
- When Diameter is run over SCTP [RFC4960] or DTLS/SCTP [RFC6083],
- which allow for connections to span multiple interfaces and multiple
- IP addresses, the Capabilities-Exchange-Request message MUST contain
- one Host-IP-Address AVP for each potential IP address that MAY be
- locally used when transmitting Diameter messages.
-
- Message Format
-
- &lt;CER> ::= &lt; Diameter Header: 257, REQ >
- { Origin-Host }
- { Origin-Realm }
- 1* { Host-IP-Address }
- { Vendor-Id }
- { Product-Name }
- [ Origin-State-Id ]
- * [ Supported-Vendor-Id ]
- * [ Auth-Application-Id ]
- * [ Inband-Security-Id ]
- * [ Acct-Application-Id ]
- * [ Vendor-Specific-Application-Id ]
- [ Firmware-Revision ]
- * [ AVP ]
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 62]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-5.3.2. Capabilities-Exchange-Answer
-
- The Capabilities-Exchange-Answer (CEA), indicated by the Command Code
- set to 257 and the Command Flags' 'R' bit cleared, is sent in
- response to a CER message.
-
- When Diameter is run over SCTP [RFC4960] or DTLS/SCTP [RFC6083],
- which allow connections to span multiple interfaces, hence, multiple
- IP addresses, the Capabilities-Exchange-Answer message MUST contain
- one Host-IP-Address AVP for each potential IP address that MAY be
- locally used when transmitting Diameter messages.
-
- Message Format
-
- &lt;CEA> ::= &lt; Diameter Header: 257 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- 1* { Host-IP-Address }
- { Vendor-Id }
- { Product-Name }
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Failed-AVP ]
- * [ Supported-Vendor-Id ]
- * [ Auth-Application-Id ]
- * [ Inband-Security-Id ]
- * [ Acct-Application-Id ]
- * [ Vendor-Specific-Application-Id ]
- [ Firmware-Revision ]
- * [ AVP ]
-
-5.3.3. Vendor-Id AVP
-
- The Vendor-Id AVP (AVP Code 266) is of type Unsigned32 and contains
- the IANA "SMI Network Management Private Enterprise Codes"
- [ENTERPRISE] value assigned to the Diameter Software vendor. It is
- envisioned that the combination of the Vendor-Id, Product-Name
- (Section 5.3.7), and Firmware-Revision (Section 5.3.4) AVPs may
- provide useful debugging information.
-
- A Vendor-Id value of zero in the CER or CEA message is reserved and
- indicates that this field is ignored.
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 63]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-5.3.4. Firmware-Revision AVP
-
- The Firmware-Revision AVP (AVP Code 267) is of type Unsigned32 and is
- used to inform a Diameter peer of the firmware revision of the
- issuing device.
-
- For devices that do not have a firmware revision (general-purpose
- computers running Diameter software modules, for instance), the
- revision of the Diameter software module may be reported instead.
-
-5.3.5. Host-IP-Address AVP
-
- The Host-IP-Address AVP (AVP Code 257) is of type Address and is used
- to inform a Diameter peer of the sender's IP address. All source
- addresses that a Diameter node expects to use with SCTP [RFC4960] or
- DTLS/SCTP [RFC6083] MUST be advertised in the CER and CEA messages by
- including a Host-IP-Address AVP for each address.
-
-5.3.6. Supported-Vendor-Id AVP
-
- The Supported-Vendor-Id AVP (AVP Code 265) is of type Unsigned32 and
- contains the IANA "SMI Network Management Private Enterprise Codes"
- [ENTERPRISE] value assigned to a vendor other than the device vendor
- but including the application vendor. This is used in the CER and
- CEA messages in order to inform the peer that the sender supports (a
- subset of) the Vendor-Specific AVPs defined by the vendor identified
- in this AVP. The value of this AVP MUST NOT be set to zero.
- Multiple instances of this AVP containing the same value SHOULD NOT
- be sent.
-
-5.3.7. Product-Name AVP
-
- The Product-Name AVP (AVP Code 269) is of type UTF8String and
- contains the vendor-assigned name for the product. The Product-Name
- AVP SHOULD remain constant across firmware revisions for the same
- product.
-
-5.4. Disconnecting Peer Connections
-
- When a Diameter node disconnects one of its transport connections,
- its peer cannot know the reason for the disconnect and will most
- likely assume that a connectivity problem occurred or that the peer
- has rebooted. In these cases, the peer may periodically attempt to
- reconnect, as stated in Section 2.1. In the event that the
- disconnect was a result of either a shortage of internal resources or
- simply that the node in question has no intentions of forwarding any
- Diameter messages to the peer in the foreseeable future, a periodic
-
-
-
-
-Fajardo, et al. Standards Track [Page 64]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- connection request would not be welcomed. The Disconnection-Reason
- AVP contains the reason the Diameter node issued the Disconnect-Peer-
- Request message.
-
- The Disconnect-Peer-Request message is used by a Diameter node to
- inform its peer of its intent to disconnect the transport layer and
- that the peer shouldn't reconnect unless it has a valid reason to do
- so (e.g., message to be forwarded). Upon receipt of the message, the
- Disconnect-Peer-Answer message is returned, which SHOULD contain an
- error if messages have recently been forwarded, and are likely in
- flight, which would otherwise cause a race condition.
-
- The receiver of the Disconnect-Peer-Answer message initiates the
- transport disconnect. The sender of the Disconnect-Peer-Answer
- message should be able to detect the transport closure and clean up
- the connection.
-
-5.4.1. Disconnect-Peer-Request
-
- The Disconnect-Peer-Request (DPR), indicated by the Command Code set
- to 282 and the Command Flags' 'R' bit set, is sent to a peer to
- inform it of its intentions to shut down the transport connection.
- Upon detection of a transport failure, this message MUST NOT be sent
- to an alternate peer.
-
- Message Format
-
- &lt;DPR> ::= &lt; Diameter Header: 282, REQ >
- { Origin-Host }
- { Origin-Realm }
- { Disconnect-Cause }
- * [ AVP ]
-
-5.4.2. Disconnect-Peer-Answer
-
- The Disconnect-Peer-Answer (DPA), indicated by the Command Code set
- to 282 and the Command Flags' 'R' bit cleared, is sent as a response
- to the Disconnect-Peer-Request message. Upon receipt of this
- message, the transport connection is shut down.
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 65]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Message Format
-
- &lt;DPA> ::= &lt; Diameter Header: 282 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ Error-Message ]
- [ Failed-AVP ]
- * [ AVP ]
-
-
-5.4.3. Disconnect-Cause AVP
-
- The Disconnect-Cause AVP (AVP Code 273) is of type Enumerated. A
- Diameter node MUST include this AVP in the Disconnect-Peer-Request
- message to inform the peer of the reason for its intention to shut
- down the transport connection. The following values are supported:
-
- REBOOTING 0
- A scheduled reboot is imminent. A receiver of a DPR with
- above result code MAY attempt reconnection.
-
- BUSY 1
- The peer's internal resources are constrained, and it has
- determined that the transport connection needs to be closed.
- A receiver of a DPR with above result code SHOULD NOT attempt
- reconnection.
-
- DO_NOT_WANT_TO_TALK_TO_YOU 2
- The peer has determined that it does not see a need for the
- transport connection to exist, since it does not expect any
- messages to be exchanged in the near future. A receiver of a
- DPR with above result code SHOULD NOT attempt reconnection.
-
-5.5. Transport Failure Detection
-
- Given the nature of the Diameter protocol, it is recommended that
- transport failures be detected as soon as possible. Detecting such
- failures will minimize the occurrence of messages sent to unavailable
- agents, resulting in unnecessary delays, and will provide better
- failover performance. The Device-Watchdog-Request and Device-
- Watchdog-Answer messages, defined in this section, are used to pro-
- actively detect transport failures.
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 66]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-5.5.1. Device-Watchdog-Request
-
- The Device-Watchdog-Request (DWR), indicated by the Command Code set
- to 280 and the Command Flags' 'R' bit set, is sent to a peer when no
- traffic has been exchanged between two peers (see Section 5.5.3).
- Upon detection of a transport failure, this message MUST NOT be sent
- to an alternate peer.
-
- Message Format
-
- &lt;DWR> ::= &lt; Diameter Header: 280, REQ >
- { Origin-Host }
- { Origin-Realm }
- [ Origin-State-Id ]
- * [ AVP ]
-
-5.5.2. Device-Watchdog-Answer
-
- The Device-Watchdog-Answer (DWA), indicated by the Command Code set
- to 280 and the Command Flags' 'R' bit cleared, is sent as a response
- to the Device-Watchdog-Request message.
-
- Message Format
-
- &lt;DWA> ::= &lt; Diameter Header: 280 >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ Error-Message ]
- [ Failed-AVP ]
- [ Origin-State-Id ]
- * [ AVP ]
-
-5.5.3. Transport Failure Algorithm
-
- The transport failure algorithm is defined in [RFC3539]. All
- Diameter implementations MUST support the algorithm defined in that
- specification in order to be compliant to the Diameter base protocol.
-
-5.5.4. Failover and Failback Procedures
-
- In the event that a transport failure is detected with a peer, it is
- necessary for all pending request messages to be forwarded to an
- alternate agent, if possible. This is commonly referred to as
- "failover".
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 67]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- In order for a Diameter node to perform failover procedures, it is
- necessary for the node to maintain a pending message queue for a
- given peer. When an answer message is received, the corresponding
- request is removed from the queue. The Hop-by-Hop Identifier field
- is used to match the answer with the queued request.
-
- When a transport failure is detected, if possible, all messages in
- the queue are sent to an alternate agent with the T flag set. On
- booting a Diameter client or agent, the T flag is also set on any
- remaining records in non-volatile storage that are still waiting to
- be transmitted. An example of a case where it is not possible to
- forward the message to an alternate server is when the message has a
- fixed destination, and the unavailable peer is the message's final
- destination (see Destination-Host AVP). Such an error requires that
- the agent return an answer message with the 'E' bit set and the
- Result-Code AVP set to DIAMETER_UNABLE_TO_DELIVER.
-
- It is important to note that multiple identical requests or answers
- MAY be received as a result of a failover. The End-to-End Identifier
- field in the Diameter header along with the Origin-Host AVP MUST be
- used to identify duplicate messages.
-
- As described in Section 2.1, a connection request should be
- periodically attempted with the failed peer in order to re-establish
- the transport connection. Once a connection has been successfully
- established, messages can once again be forwarded to the peer. This
- is commonly referred to as "failback".
-
-5.6. Peer State Machine
-
- This section contains a finite state machine that MUST be observed by
- all Diameter implementations. Each Diameter node MUST follow the
- state machine described below when communicating with each peer.
- Multiple actions are separated by commas, and may continue on
- succeeding lines, as space requires. Similarly, state and next state
- may also span multiple lines, as space requires.
-
- This state machine is closely coupled with the state machine
- described in [RFC3539], which is used to open, close, failover,
- probe, and reopen transport connections. In particular, note that
- [RFC3539] requires the use of watchdog messages to probe connections.
- For Diameter, DWR and DWA messages are to be used.
-
- The I- prefix is used to represent the initiator (connecting)
- connection, while the R- prefix is used to represent the responder
- (listening) connection. The lack of a prefix indicates that the
- event or action is the same regardless of the connection on which the
- event occurred.
-
-
-
-Fajardo, et al. Standards Track [Page 68]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The stable states that a state machine may be in are Closed, I-Open,
- and R-Open; all other states are intermediate. Note that I-Open and
- R-Open are equivalent except for whether the initiator or responder
- transport connection is used for communication.
-
- A CER message is always sent on the initiating connection immediately
- after the connection request is successfully completed. In the case
- of an election, one of the two connections will shut down. The
- responder connection will survive if the Origin-Host of the local
- Diameter entity is higher than that of the peer; the initiator
- connection will survive if the peer's Origin-Host is higher. All
- subsequent messages are sent on the surviving connection. Note that
- the results of an election on one peer are guaranteed to be the
- inverse of the results on the other.
-
- For TLS/TCP and DTLS/SCTP usage, a TLS/TCP and DTLS/SCTP handshake
- SHOULD begin when both ends are in the closed state prior to any
- Diameter message exchanges. The TLS/TCP and DTLS/SCTP connection
- SHOULD be established before sending any CER or CEA message to secure
- and protect the capabilities information of both peers. The TLS/TCP
- and DTLS/SCTP connection SHOULD be disconnected when the state
- machine moves to the closed state. When connecting to responders
- that do not conform to this document (i.e., older Diameter
- implementations that are not prepared to received TLS/TCP and DTLS/
- SCTP connections in the closed state), the initial TLS/TCP and DTLS/
- SCTP connection attempt will fail. The initiator MAY then attempt to
- connect via TCP or SCTP and initiate the TLS/TCP and DTLS/SCTP
- handshake when both ends are in the open state. If the handshake is
- successful, all further messages will be sent via TLS/TCP and DTLS/
- SCTP. If the handshake fails, both ends move to the closed state.
-
- The state machine constrains only the behavior of a Diameter
- implementation as seen by Diameter peers through events on the wire.
-
- Any implementation that produces equivalent results is considered
- compliant.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 69]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- state event action next state
- -----------------------------------------------------------------
- Closed Start I-Snd-Conn-Req Wait-Conn-Ack
- R-Conn-CER R-Accept, R-Open
- Process-CER,
- R-Snd-CEA
-
- Wait-Conn-Ack I-Rcv-Conn-Ack I-Snd-CER Wait-I-CEA
- I-Rcv-Conn-Nack Cleanup Closed
- R-Conn-CER R-Accept, Wait-Conn-Ack/
- Process-CER Elect
- Timeout Error Closed
-
- Wait-I-CEA I-Rcv-CEA Process-CEA I-Open
- R-Conn-CER R-Accept, Wait-Returns
- Process-CER,
- Elect
- I-Peer-Disc I-Disc Closed
- I-Rcv-Non-CEA Error Closed
- Timeout Error Closed
-
- Wait-Conn-Ack/ I-Rcv-Conn-Ack I-Snd-CER,Elect Wait-Returns
- Elect I-Rcv-Conn-Nack R-Snd-CEA R-Open
- R-Peer-Disc R-Disc Wait-Conn-Ack
- R-Conn-CER R-Reject Wait-Conn-Ack/
- Elect
- Timeout Error Closed
-
- Wait-Returns Win-Election I-Disc,R-Snd-CEA R-Open
- I-Peer-Disc I-Disc, R-Open
- R-Snd-CEA
- I-Rcv-CEA R-Disc I-Open
- R-Peer-Disc R-Disc Wait-I-CEA
- R-Conn-CER R-Reject Wait-Returns
- Timeout Error Closed
-
- R-Open Send-Message R-Snd-Message R-Open
- R-Rcv-Message Process R-Open
- R-Rcv-DWR Process-DWR, R-Open
- R-Snd-DWA
- R-Rcv-DWA Process-DWA R-Open
- R-Conn-CER R-Reject R-Open
- Stop R-Snd-DPR Closing
- R-Rcv-DPR R-Snd-DPA Closing
- R-Peer-Disc R-Disc Closed
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 70]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- I-Open Send-Message I-Snd-Message I-Open
- I-Rcv-Message Process I-Open
- I-Rcv-DWR Process-DWR, I-Open
- I-Snd-DWA
- I-Rcv-DWA Process-DWA I-Open
- R-Conn-CER R-Reject I-Open
- Stop I-Snd-DPR Closing
- I-Rcv-DPR I-Snd-DPA Closing
- I-Peer-Disc I-Disc Closed
-
- Closing I-Rcv-DPA I-Disc Closed
- R-Rcv-DPA R-Disc Closed
- Timeout Error Closed
- I-Peer-Disc I-Disc Closed
- R-Peer-Disc R-Disc Closed
-
-5.6.1. Incoming Connections
-
- When a connection request is received from a Diameter peer, it is
- not, in the general case, possible to know the identity of that peer
- until a CER is received from it. This is because host and port
- determine the identity of a Diameter peer; the source port of an
- incoming connection is arbitrary. Upon receipt of a CER, the
- identity of the connecting peer can be uniquely determined from the
- Origin-Host.
-
- For this reason, a Diameter peer must employ logic separate from the
- state machine to receive connection requests, accept them, and await
- the CER. Once the CER arrives on a new connection, the Origin-Host
- that identifies the peer is used to locate the state machine
- associated with that peer, and the new connection and CER are passed
- to the state machine as an R-Conn-CER event.
-
- The logic that handles incoming connections SHOULD close and discard
- the connection if any message other than a CER arrives or if an
- implementation-defined timeout occurs prior to receipt of CER.
-
- Because handling of incoming connections up to and including receipt
- of a CER requires logic, separate from that of any individual state
- machine associated with a particular peer, it is described separately
- in this section rather than in the state machine above.
-
-5.6.2. Events
-
- Transitions and actions in the automaton are caused by events. In
- this section, we will ignore the I- and R- prefixes, since the actual
- event would be identical, but it would occur on one of two possible
- connections.
-
-
-
-Fajardo, et al. Standards Track [Page 71]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Start The Diameter application has signaled that a
- connection should be initiated with the peer.
-
- R-Conn-CER An acknowledgement is received stating that the
- transport connection has been established, and the
- associated CER has arrived.
-
- Rcv-Conn-Ack A positive acknowledgement is received confirming that
- the transport connection is established.
-
- Rcv-Conn-Nack A negative acknowledgement was received stating that
- the transport connection was not established.
-
- Timeout An application-defined timer has expired while waiting
- for some event.
-
- Rcv-CER A CER message from the peer was received.
-
- Rcv-CEA A CEA message from the peer was received.
-
- Rcv-Non-CEA A message, other than a CEA, from the peer was
- received.
-
- Peer-Disc A disconnection indication from the peer was received.
-
- Rcv-DPR A DPR message from the peer was received.
-
- Rcv-DPA A DPA message from the peer was received.
-
- Win-Election An election was held, and the local node was the
- winner.
-
- Send-Message A message is to be sent.
-
- Rcv-Message A message other than CER, CEA, DPR, DPA, DWR, or DWA
- was received.
-
- Stop The Diameter application has signaled that a
- connection should be terminated (e.g., on system
- shutdown).
-
-5.6.3. Actions
-
- Actions in the automaton are caused by events and typically indicate
- the transmission of packets and/or an action to be taken on the
- connection. In this section, we will ignore the I- and R- prefixes,
- since the actual action would be identical, but it would occur on one
- of two possible connections.
-
-
-
-Fajardo, et al. Standards Track [Page 72]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Snd-Conn-Req A transport connection is initiated with the peer.
-
- Accept The incoming connection associated with the R-Conn-CER
- is accepted as the responder connection.
-
- Reject The incoming connection associated with the R-Conn-CER
- is disconnected.
-
- Process-CER The CER associated with the R-Conn-CER is processed.
-
- Snd-CER A CER message is sent to the peer.
-
- Snd-CEA A CEA message is sent to the peer.
-
- Cleanup If necessary, the connection is shut down, and any
- local resources are freed.
-
- Error The transport layer connection is disconnected,
- either politely or abortively, in response to
- an error condition. Local resources are freed.
-
- Process-CEA A received CEA is processed.
-
- Snd-DPR A DPR message is sent to the peer.
-
- Snd-DPA A DPA message is sent to the peer.
-
- Disc The transport layer connection is disconnected,
- and local resources are freed.
-
- Elect An election occurs (see Section 5.6.4 for more
- information).
-
- Snd-Message A message is sent.
-
- Snd-DWR A DWR message is sent.
-
- Snd-DWA A DWA message is sent.
-
- Process-DWR The DWR message is serviced.
-
- Process-DWA The DWA message is serviced.
-
- Process A message is serviced.
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 73]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-5.6.4. The Election Process
-
- The election is performed on the responder. The responder compares
- the Origin-Host received in the CER with its own Origin-Host as two
- streams of octets. If the local Origin-Host lexicographically
- succeeds the received Origin-Host, a Win-Election event is issued
- locally. Diameter identities are in ASCII form; therefore, the
- lexical comparison is consistent with DNS case insensitivity, where
- octets that fall in the ASCII range 'a' through 'z' MUST compare
- equally to their uppercase counterparts between 'A' and 'Z'. See
- Appendix D for interactions between the Diameter protocol and
- Internationalized Domain Name (IDNs).
-
- The winner of the election MUST close the connection it initiated.
- Historically, maintaining the responder side of a connection was more
- efficient than maintaining the initiator side. However, current
- practices makes this distinction irrelevant.
-
-6. Diameter Message Processing
-
- This section describes how Diameter requests and answers are created
- and processed.
-
-6.1. Diameter Request Routing Overview
-
- A request is sent towards its final destination using one of the
- following three combinations of the Destination-Realm and
- Destination-Host AVPs:
-
- o A request that is not able to be proxied (such as a CER) MUST NOT
- contain either Destination-Realm or Destination-Host AVPs.
-
- o A request that needs to be sent to a home server serving a
- specific realm, but not to a specific server (such as the first
- request of a series of round trips), MUST contain a Destination-
- Realm AVP but MUST NOT contain a Destination-Host AVP. For
- Diameter clients, the value of the Destination-Realm AVP MAY be
- extracted from the User-Name AVP, or other methods.
-
- o Otherwise, a request that needs to be sent to a specific home
- server among those serving a given realm MUST contain both the
- Destination-Realm and Destination-Host AVPs.
-
- The Destination-Host AVP is used as described above when the
- destination of the request is fixed, which includes:
-
- o Authentication requests that span multiple round trips.
-
-
-
-
-Fajardo, et al. Standards Track [Page 74]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o A Diameter message that uses a security mechanism that makes use
- of a pre-established session key shared between the source and the
- final destination of the message.
-
- o Server-initiated messages that MUST be received by a specific
- Diameter client (e.g., access device), such as the Abort-Session-
- Request message, which is used to request that a particular user's
- session be terminated.
-
- Note that an agent can only forward a request to a host described in
- the Destination-Host AVP if the host in question is included in its
- peer table (see Section 2.6). Otherwise, the request is routed based
- on the Destination-Realm only (see Section 6.1.6).
-
- When a message is received, the message is processed in the following
- order:
-
- o If the message is destined for the local host, the procedures
- listed in Section 6.1.4 are followed.
-
- o If the message is intended for a Diameter peer with whom the local
- host is able to directly communicate, the procedures listed in
- Section 6.1.5 are followed. This is known as "Request
- Forwarding".
-
- o The procedure listed in Section 6.1.6 is followed, which is known
- as "Request Routing".
-
- o If none of the above are successful, an answer is returned with
- the Result-Code set to DIAMETER_UNABLE_TO_DELIVER, with the 'E'
- bit set.
-
- For routing of Diameter messages to work within an administrative
- domain, all Diameter nodes within the realm MUST be peers.
-
- The overview contained in this section (6.1) is intended to provide
- general guidelines to Diameter developers. Implementations are free
- to use different methods than the ones described here as long as they
- conform to the requirements specified in Sections 6.1.1 through
- 6.1.9. See Section 7 for more details on error handling.
-
-6.1.1. Originating a Request
-
- When creating a request, in addition to any other procedures
- described in the application definition for that specific request,
- the following procedures MUST be followed:
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 75]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o the Command Code is set to the appropriate value;
-
- o the 'R' bit is set;
-
- o the End-to-End Identifier is set to a locally unique value;
-
- o the Origin-Host and Origin-Realm AVPs MUST be set to the
- appropriate values, used to identify the source of the message;
- and
-
- o the Destination-Host and Destination-Realm AVPs MUST be set to the
- appropriate values, as described in Section 6.1.
-
-6.1.2. Sending a Request
-
- When sending a request, originated either locally or as the result of
- a forwarding or routing operation, the following procedures SHOULD be
- followed:
-
- o The Hop-by-Hop Identifier SHOULD be set to a locally unique value.
-
- o The message SHOULD be saved in the list of pending requests.
-
- Other actions to perform on the message based on the particular role
- the agent is playing are described in the following sections.
-
-6.1.3. Receiving Requests
-
- A relay or proxy agent MUST check for forwarding loops when receiving
- requests. A loop is detected if the server finds its own identity in
- a Route-Record AVP. When such an event occurs, the agent MUST answer
- with the Result-Code AVP set to DIAMETER_LOOP_DETECTED.
-
-6.1.4. Processing Local Requests
-
- A request is known to be for local consumption when one of the
- following conditions occurs:
-
- o The Destination-Host AVP contains the local host's identity;
-
- o The Destination-Host AVP is not present, the Destination-Realm AVP
- contains a realm the server is configured to process locally, and
- the Diameter application is locally supported; or
-
- o Both the Destination-Host and the Destination-Realm are not
- present.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 76]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- When a request is locally processed, the rules in Section 6.2 should
- be used to generate the corresponding answer.
-
-6.1.5. Request Forwarding
-
- Request forwarding is done using the Diameter peer table. The
- Diameter peer table contains all of the peers with which the local
- node is able to directly communicate.
-
- When a request is received, and the host encoded in the Destination-
- Host AVP is one that is present in the peer table, the message SHOULD
- be forwarded to the peer.
-
-6.1.6. Request Routing
-
- Diameter request message routing is done via realms and Application
- Ids. A Diameter message that may be forwarded by Diameter agents
- (proxies, redirect agents, or relay agents) MUST include the target
- realm in the Destination-Realm AVP. Request routing SHOULD rely on
- the Destination-Realm AVP and the Application Id present in the
- request message header to aid in the routing decision. The realm MAY
- be retrieved from the User-Name AVP, which is in the form of a
- Network Access Identifier (NAI). The realm portion of the NAI is
- inserted in the Destination-Realm AVP.
-
- Diameter agents MAY have a list of locally supported realms and
- applications, and they MAY have a list of externally supported realms
- and applications. When a request is received that includes a realm
- and/or application that is not locally supported, the message is
- routed to the peer configured in the routing table (see Section 2.7).
-
- Realm names and Application Ids are the minimum supported routing
- criteria, additional information may be needed to support redirect
- semantics.
-
-6.1.7. Predictive Loop Avoidance
-
- Before forwarding or routing a request, Diameter agents, in addition
- to performing the processing described in Section 6.1.3, SHOULD check
- for the presence of a candidate route's peer identity in any of the
- Route-Record AVPs. In the event of the agent detecting the presence
- of a candidate route's peer identity in a Route-Record AVP, the agent
- MUST ignore such a route for the Diameter request message and attempt
- alternate routes if any exist. In case all the candidate routes are
- eliminated by the above criteria, the agent SHOULD return a
- DIAMETER_UNABLE_TO_DELIVER message.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 77]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-6.1.8. Redirecting Requests
-
- When a redirect agent receives a request whose routing entry is set
- to REDIRECT, it MUST reply with an answer message with the 'E' bit
- set, while maintaining the Hop-by-Hop Identifier in the header, and
- include the Result-Code AVP to DIAMETER_REDIRECT_INDICATION. Each of
- the servers associated with the routing entry are added in a separate
- Redirect-Host AVP.
-
- +------------------+
- | Diameter |
- | Redirect Agent |
- +------------------+
- ^ | 2. command + 'E' bit
- 1. Request | | Result-Code =
- [email protected] | | DIAMETER_REDIRECT_INDICATION +
- | | Redirect-Host AVP(s)
- | v
- +-------------+ 3. Request +-------------+
- | example.com |------------->| example.net |
- | Relay | | Diameter |
- | Agent |&lt;-------------| Server |
- +-------------+ 4. Answer +-------------+
-
- Figure 5: Diameter Redirect Agent
-
- The receiver of an answer message with the 'E' bit set and the
- Result-Code AVP set to DIAMETER_REDIRECT_INDICATION uses the Hop-by-
- Hop Identifier in the Diameter header to identify the request in the
- pending message queue (see Section 5.5.4) that is to be redirected.
- If no transport connection exists with the new peer, one is created,
- and the request is sent directly to it.
-
- Multiple Redirect-Host AVPs are allowed. The receiver of the answer
- message with the 'E' bit set selects exactly one of these hosts as
- the destination of the redirected message.
-
- When the Redirect-Host-Usage AVP included in the answer message has a
- non-zero value, a route entry for the redirect indications is created
- and cached by the receiver. The redirect usage for such a route
- entry is set by the value of Redirect-Host-Usage AVP and the lifetime
- of the cached route entry is set by Redirect-Max-Cache-Time AVP
- value.
-
- It is possible that multiple redirect indications can create multiple
- cached route entries differing only in their redirect usage and the
- peer to forward messages to. As an example, two(2) route entries
- that are created by two(2) redirect indications results in two(2)
-
-
-
-Fajardo, et al. Standards Track [Page 78]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- cached routes for the same realm and Application Id. However, one
- has a redirect usage of ALL_SESSION, where matching requests will be
- forwarded to one peer; the other has a redirect usage of ALL_REALM,
- where request are forwarded to another peer. Therefore, an incoming
- request that matches the realm and Application Id of both routes will
- need additional resolution. In such a case, a routing precedence
- rule MUST be used against the redirect usage value to resolve the
- contention. The precedence rule can be found in Section 6.13.
-
-6.1.9. Relaying and Proxying Requests
-
- A relay or proxy agent MUST append a Route-Record AVP to all requests
- forwarded. The AVP contains the identity of the peer from which the
- request was received.
-
- The Hop-by-Hop Identifier in the request is saved and replaced with a
- locally unique value. The source of the request is also saved, which
- includes the IP address, port, and protocol.
-
- A relay or proxy agent MAY include the Proxy-Info AVP in requests if
- it requires access to any local state information when the
- corresponding response is received. The Proxy-Info AVP has security
- implications as state information is distributed to other entities.
- As such, it is RECOMMENDED that the content of the Proxy-Info AVP be
- protected with cryptographic mechanisms, for example, by using a
- keyed message digest such as HMAC-SHA1 [RFC2104]. Such a mechanism,
- however, requires the management of keys, although only locally at
- the Diameter server. Still, a full description of the management of
- the keys used to protect the Proxy-Info AVP is beyond the scope of
- this document. Below is a list of common recommendations:
-
- o The keys should be generated securely following the randomness
- recommendations in [RFC4086].
-
- o The keys and cryptographic protection algorithms should be at
- least 128 bits in strength.
-
- o The keys should not be used for any other purpose than generating
- and verifying instances of the Proxy-Info AVP.
-
- o The keys should be changed regularly.
-
- o The keys should be changed if the AVP format or cryptographic
- protection algorithms change.
-
- The message is then forwarded to the next hop, as identified in the
- routing table.
-
-
-
-
-Fajardo, et al. Standards Track [Page 79]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Figure 6 provides an example of message routing using the procedures
- listed in these sections.
-
- (Origin-Host=nas.example.net) (Origin-Host=nas.example.net)
- (Origin-Realm=example.net) (Origin-Realm=example.net)
- (Destination-Realm=example.com) (Destination-Realm=example.com)
- (Route-Record=nas.example.net)
- +------+ ------> +------+ ------> +------+
- | | (Request) | | (Request) | |
- | NAS +-------------------+ DRL +-------------------+ HMS |
- | | | | | |
- +------+ &lt;------ +------+ &lt;------ +------+
- example.net (Answer) example.net (Answer) example.com
- (Origin-Host=hms.example.com) (Origin-Host=hms.example.com)
- (Origin-Realm=example.com) (Origin-Realm=example.com)
-
- Figure 6: Routing of Diameter messages
-
- Relay and proxy agents are not required to perform full inspection of
- incoming messages. At a minimum, validation of the message header
- and relevant routing AVPs has to be done when relaying messages.
- Proxy agents may optionally perform more in-depth message validation
- for applications in which it is interested.
-
-6.2. Diameter Answer Processing
-
- When a request is locally processed, the following procedures MUST be
- applied to create the associated answer, in addition to any
- additional procedures that MAY be discussed in the Diameter
- application defining the command:
-
- o The same Hop-by-Hop Identifier in the request is used in the
- answer.
-
- o The local host's identity is encoded in the Origin-Host AVP.
-
- o The Destination-Host and Destination-Realm AVPs MUST NOT be
- present in the answer message.
-
- o The Result-Code AVP is added with its value indicating success or
- failure.
-
- o If the Session-Id is present in the request, it MUST be included
- in the answer.
-
- o Any Proxy-Info AVPs in the request MUST be added to the answer
- message, in the same order they were present in the request.
-
-
-
-
-Fajardo, et al. Standards Track [Page 80]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o The 'P' bit is set to the same value as the one in the request.
-
- o The same End-to-End identifier in the request is used in the
- answer.
-
- Note that the error messages (see Section 7) are also subjected to
- the above processing rules.
-
-6.2.1. Processing Received Answers
-
- A Diameter client or proxy MUST match the Hop-by-Hop Identifier in an
- answer received against the list of pending requests. The
- corresponding message should be removed from the list of pending
- requests. It SHOULD ignore answers received that do not match a
- known Hop-by-Hop Identifier.
-
-6.2.2. Relaying and Proxying Answers
-
- If the answer is for a request that was proxied or relayed, the agent
- MUST restore the original value of the Diameter header's Hop-by-Hop
- Identifier field.
-
- If the last Proxy-Info AVP in the message is targeted to the local
- Diameter server, the AVP MUST be removed before the answer is
- forwarded.
-
- If a relay or proxy agent receives an answer with a Result-Code AVP
- indicating a failure, it MUST NOT modify the contents of the AVP.
- Any additional local errors detected SHOULD be logged but not
- reflected in the Result-Code AVP. If the agent receives an answer
- message with a Result-Code AVP indicating success, and it wishes to
- modify the AVP to indicate an error, it MUST modify the Result-Code
- AVP to contain the appropriate error in the message destined towards
- the access device as well as include the Error-Reporting-Host AVP; it
- MUST also issue an STR on behalf of the access device towards the
- Diameter server.
-
- The agent MUST then send the answer to the host that it received the
- original request from.
-
-6.3. Origin-Host AVP
-
- The Origin-Host AVP (AVP Code 264) is of type DiameterIdentity, and
- it MUST be present in all Diameter messages. This AVP identifies the
- endpoint that originated the Diameter message. Relay agents MUST NOT
- modify this AVP.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 81]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The value of the Origin-Host AVP is guaranteed to be unique within a
- single host.
-
- Note that the Origin-Host AVP may resolve to more than one address as
- the Diameter peer may support more than one address.
-
- This AVP SHOULD be placed as close to the Diameter header as
- possible.
-
-6.4. Origin-Realm AVP
-
- The Origin-Realm AVP (AVP Code 296) is of type DiameterIdentity.
- This AVP contains the Realm of the originator of any Diameter message
- and MUST be present in all messages.
-
- This AVP SHOULD be placed as close to the Diameter header as
- possible.
-
-6.5. Destination-Host AVP
-
- The Destination-Host AVP (AVP Code 293) is of type DiameterIdentity.
- This AVP MUST be present in all unsolicited agent initiated messages,
- MAY be present in request messages, and MUST NOT be present in answer
- messages.
-
- The absence of the Destination-Host AVP will cause a message to be
- sent to any Diameter server supporting the application within the
- realm specified in Destination-Realm AVP.
-
- This AVP SHOULD be placed as close to the Diameter header as
- possible.
-
-6.6. Destination-Realm AVP
-
- The Destination-Realm AVP (AVP Code 283) is of type DiameterIdentity
- and contains the realm to which the message is to be routed. The
- Destination-Realm AVP MUST NOT be present in answer messages.
- Diameter clients insert the realm portion of the User-Name AVP.
- Diameter servers initiating a request message use the value of the
- Origin-Realm AVP from a previous message received from the intended
- target host (unless it is known a priori). When present, the
- Destination-Realm AVP is used to perform message routing decisions.
-
- The CCF for a request message that includes the Destination-Realm AVP
- SHOULD list the Destination-Realm AVP as a required AVP (an AVP
- indicated as {AVP}); otherwise, the message is inherently a non-
- routable message.
-
-
-
-
-Fajardo, et al. Standards Track [Page 82]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- This AVP SHOULD be placed as close to the Diameter header as
- possible.
-
-6.7. Routing AVPs
-
- The AVPs defined in this section are Diameter AVPs used for routing
- purposes. These AVPs change as Diameter messages are processed by
- agents.
-
-6.7.1. Route-Record AVP
-
- The Route-Record AVP (AVP Code 282) is of type DiameterIdentity. The
- identity added in this AVP MUST be the same as the one received in
- the Origin-Host of the Capabilities Exchange message.
-
-6.7.2. Proxy-Info AVP
-
- The Proxy-Info AVP (AVP Code 284) is of type Grouped. This AVP
- contains the identity and local state information of the Diameter
- node that creates and adds it to a message. The Grouped Data field
- has the following CCF grammar:
-
- Proxy-Info ::= &lt; AVP Header: 284 >
- { Proxy-Host }
- { Proxy-State }
- * [ AVP ]
-
-6.7.3. Proxy-Host AVP
-
- The Proxy-Host AVP (AVP Code 280) is of type DiameterIdentity. This
- AVP contains the identity of the host that added the Proxy-Info AVP.
-
-6.7.4. Proxy-State AVP
-
- The Proxy-State AVP (AVP Code 33) is of type OctetString. It
- contains state information that would otherwise be stored at the
- Diameter entity that created it. As such, this AVP MUST be treated
- as opaque data by other Diameter entities.
-
-6.8. Auth-Application-Id AVP
-
- The Auth-Application-Id AVP (AVP Code 258) is of type Unsigned32 and
- is used in order to advertise support of the Authentication and
- Authorization portion of an application (see Section 2.4). If
- present in a message other than CER and CEA, the value of the Auth-
- Application-Id AVP MUST match the Application Id present in the
- Diameter message header.
-
-
-
-
-Fajardo, et al. Standards Track [Page 83]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-6.9. Acct-Application-Id AVP
-
- The Acct-Application-Id AVP (AVP Code 259) is of type Unsigned32 and
- is used in order to advertise support of the accounting portion of an
- application (see Section 2.4). If present in a message other than
- CER and CEA, the value of the Acct-Application-Id AVP MUST match the
- Application Id present in the Diameter message header.
-
-6.10. Inband-Security-Id AVP
-
- The Inband-Security-Id AVP (AVP Code 299) is of type Unsigned32 and
- is used in order to advertise support of the security portion of the
- application. The use of this AVP in CER and CEA messages is NOT
- RECOMMENDED. Instead, discovery of a Diameter entity's security
- capabilities can be done either through static configuration or via
- Diameter Peer Discovery as described in Section 5.2.
-
- The following values are supported:
-
-
- NO_INBAND_SECURITY 0
-
- This peer does not support TLS/TCP and DTLS/SCTP. This is the
- default value, if the AVP is omitted.
-
- TLS 1
-
- This node supports TLS/TCP [RFC5246] and DTLS/SCTP [RFC6083]
- security.
-
-6.11. Vendor-Specific-Application-Id AVP
-
- The Vendor-Specific-Application-Id AVP (AVP Code 260) is of type
- Grouped and is used to advertise support of a vendor-specific
- Diameter application. Exactly one instance of either Auth-
- Application-Id or Acct-Application-Id AVP MUST be present. The
- Application Id carried by either Auth-Application-Id or Acct-
- Application-Id AVP MUST comply with vendor-specific Application Id
- assignment described in Section 11.3. It MUST also match the
- Application Id present in the Diameter header except when used in a
- CER or CEA message.
-
- The Vendor-Id AVP is an informational AVP pertaining to the vendor
- who may have authorship of the vendor-specific Diameter application.
- It MUST NOT be used as a means of defining a completely separate
- vendor-specific Application Id space.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 84]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The Vendor-Specific-Application-Id AVP SHOULD be placed as close to
- the Diameter header as possible.
-
- AVP Format
-
- &lt;Vendor-Specific-Application-Id> ::= &lt; AVP Header: 260 >
- { Vendor-Id }
- [ Auth-Application-Id ]
- [ Acct-Application-Id ]
-
- A Vendor-Specific-Application-Id AVP MUST contain exactly one of
- either Auth-Application-Id or Acct-Application-Id. If a Vendor-
- Specific-Application-Id is received without one of these two AVPs,
- then the recipient SHOULD issue an answer with a Result-Code set to
- DIAMETER_MISSING_AVP. The answer SHOULD also include a Failed-AVP,
- which MUST contain an example of an Auth-Application-Id AVP and an
- Acct-Application-Id AVP.
-
- If a Vendor-Specific-Application-Id is received that contains both
- Auth-Application-Id and Acct-Application-Id, then the recipient MUST
- issue an answer with Result-Code set to
- DIAMETER_AVP_OCCURS_TOO_MANY_TIMES. The answer MUST also include a
- Failed-AVP, which MUST contain the received Auth-Application-Id AVP
- and Acct-Application-Id AVP.
-
-6.12. Redirect-Host AVP
-
- The Redirect-Host AVP (AVP Code 292) is of type DiameterURI. One or
- more instances of this AVP MUST be present if the answer message's
- 'E' bit is set and the Result-Code AVP is set to
- DIAMETER_REDIRECT_INDICATION.
-
- Upon receiving the above, the receiving Diameter node SHOULD forward
- the request directly to one of the hosts identified in these AVPs.
- The server contained in the selected Redirect-Host AVP SHOULD be used
- for all messages matching the criteria set by the Redirect-Host-Usage
- AVP.
-
-6.13. Redirect-Host-Usage AVP
-
- The Redirect-Host-Usage AVP (AVP Code 261) is of type Enumerated.
- This AVP MAY be present in answer messages whose 'E' bit is set and
- the Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION.
-
- When present, this AVP provides hints about how the routing entry
- resulting from the Redirect-Host is to be used. The following values
- are supported:
-
-
-
-
-Fajardo, et al. Standards Track [Page 85]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- DONT_CACHE 0
-
- The host specified in the Redirect-Host AVP SHOULD NOT be cached.
- This is the default value.
-
- ALL_SESSION 1
-
- All messages within the same session, as defined by the same value
- of the Session-ID AVP SHOULD be sent to the host specified in the
- Redirect-Host AVP.
-
- ALL_REALM 2
-
- All messages destined for the realm requested SHOULD be sent to
- the host specified in the Redirect-Host AVP.
-
- REALM_AND_APPLICATION 3
-
- All messages for the application requested to the realm specified
- SHOULD be sent to the host specified in the Redirect-Host AVP.
-
- ALL_APPLICATION 4
-
- All messages for the application requested SHOULD be sent to the
- host specified in the Redirect-Host AVP.
-
- ALL_HOST 5
-
- All messages that would be sent to the host that generated the
- Redirect-Host SHOULD be sent to the host specified in the
- Redirect-Host AVP.
-
- ALL_USER 6
-
- All messages for the user requested SHOULD be sent to the host
- specified in the Redirect-Host AVP.
-
- When multiple cached routes are created by redirect indications and
- they differ only in redirect usage and peers to forward requests to
- (see Section 6.1.8), a precedence rule MUST be applied to the
- redirect usage values of the cached routes during normal routing to
- resolve contentions that may occur. The precedence rule is the order
- that dictate which redirect usage should be considered before any
- other as they appear. The order is as follows:
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 86]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- 1. ALL_SESSION
-
- 2. ALL_USER
-
- 3. REALM_AND_APPLICATION
-
- 4. ALL_REALM
-
- 5. ALL_APPLICATION
-
- 6. ALL_HOST
-
-6.14. Redirect-Max-Cache-Time AVP
-
- The Redirect-Max-Cache-Time AVP (AVP Code 262) is of type Unsigned32.
- This AVP MUST be present in answer messages whose 'E' bit is set,
- whose Result-Code AVP is set to DIAMETER_REDIRECT_INDICATION, and
- whose Redirect-Host-Usage AVP set to a non-zero value.
-
- This AVP contains the maximum number of seconds the peer and route
- table entries, created as a result of the Redirect-Host, SHOULD be
- cached. Note that once a host is no longer reachable, any associated
- cache, peer, and routing table entries MUST be deleted.
-
-7. Error Handling
-
- There are two different types of errors in Diameter; protocol errors
- and application errors. A protocol error is one that occurs at the
- base protocol level and MAY require per-hop attention (e.g., a
- message routing error). Application errors, on the other hand,
- generally occur due to a problem with a function specified in a
- Diameter application (e.g., user authentication, missing AVP).
-
- Result-Code AVP values that are used to report protocol errors MUST
- only be present in answer messages whose 'E' bit is set. When a
- request message is received that causes a protocol error, an answer
- message is returned with the 'E' bit set, and the Result-Code AVP is
- set to the appropriate protocol error value. As the answer is sent
- back towards the originator of the request, each proxy or relay agent
- MAY take action on the message.
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 87]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- 1. Request +---------+ Link Broken
- +-------------------------->|Diameter |----///----+
- | +---------------------| | v
- +------+--+ | 2. answer + 'E' set | Relay 2 | +--------+
- |Diameter |&lt;-+ (Unable to Forward) +---------+ |Diameter|
- | | | Home |
- | Relay 1 |--+ +---------+ | Server |
- +---------+ | 3. Request |Diameter | +--------+
- +-------------------->| | ^
- | Relay 3 |-----------+
- +---------+
-
- Figure 7: Example of Protocol Error Causing Answer Message
-
- Figure 7 provides an example of a message forwarded upstream by a
- Diameter relay. When the message is received by Relay 2, and it
- detects that it cannot forward the request to the home server, an
- answer message is returned with the 'E' bit set and the Result-Code
- AVP set to DIAMETER_UNABLE_TO_DELIVER. Given that this error falls
- within the protocol error category, Relay 1 would take special
- action, and given the error, attempt to route the message through its
- alternate Relay 3.
-
- +---------+ 1. Request +---------+ 2. Request +---------+
- | Access |------------>|Diameter |------------>|Diameter |
- | | | | | Home |
- | Device |&lt;------------| Relay |&lt;------------| Server |
- +---------+ 4. Answer +---------+ 3. Answer +---------+
- (Missing AVP) (Missing AVP)
-
- Figure 8: Example of Application Error Answer Message
-
- Figure 8 provides an example of a Diameter message that caused an
- application error. When application errors occur, the Diameter
- entity reporting the error clears the 'R' bit in the Command Flags
- and adds the Result-Code AVP with the proper value. Application
- errors do not require any proxy or relay agent involvement;
- therefore, the message would be forwarded back to the originator of
- the request.
-
- In the case where the answer message itself contains errors, any
- related session SHOULD be terminated by sending an STR or ASR
- message. The Termination-Cause AVP in the STR MAY be filled with the
- appropriate value to indicate the cause of the error. An application
- MAY also send an application-specific request instead of an STR or
- ASR message to signal the error in the case where no state is
- maintained or to allow for some form of error recovery with the
- corresponding Diameter entity.
-
-
-
-Fajardo, et al. Standards Track [Page 88]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- There are certain Result-Code AVP application errors that require
- additional AVPs to be present in the answer. In these cases, the
- Diameter node that sets the Result-Code AVP to indicate the error
- MUST add the AVPs. Examples are as follows:
-
- o A request with an unrecognized AVP is received with the 'M' bit
- (Mandatory bit) set causes an answer to be sent with the Result-
- Code AVP set to DIAMETER_AVP_UNSUPPORTED and the Failed-AVP AVP
- containing the offending AVP.
-
- o A request with an AVP that is received with an unrecognized value
- causes an answer to be returned with the Result-Code AVP set to
- DIAMETER_INVALID_AVP_VALUE, with the Failed-AVP AVP containing the
- AVP causing the error.
-
- o A received command that is missing AVPs that are defined as
- required in the commands CCF; examples are AVPs indicated as
- {AVP}. The receiver issues an answer with the Result-Code set to
- DIAMETER_MISSING_AVP and creates an AVP with the AVP Code and
- other fields set as expected in the missing AVP. The created AVP
- is then added to the Failed-AVP AVP.
-
- The Result-Code AVP describes the error that the Diameter node
- encountered in its processing. In case there are multiple errors,
- the Diameter node MUST report only the first error it encountered
- (detected possibly in some implementation-dependent order). The
- specific errors that can be described by this AVP are described in
- the following section.
-
-7.1. Result-Code AVP
-
- The Result-Code AVP (AVP Code 268) is of type Unsigned32 and
- indicates whether a particular request was completed successfully or
- an error occurred. All Diameter answer messages in IETF-defined
- Diameter application specifications MUST include one Result-Code AVP.
- A non-successful Result-Code AVP (one containing a non-2xxx value
- other than DIAMETER_REDIRECT_INDICATION) MUST include the Error-
- Reporting-Host AVP if the host setting the Result-Code AVP is
- different from the identity encoded in the Origin-Host AVP.
-
- The Result-Code data field contains an IANA-managed 32-bit address
- space representing errors (see Section 11.3.2). Diameter provides
- the following classes of errors, all identified by the thousands
- digit in the decimal notation:
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 89]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o 1xxx (Informational)
-
- o 2xxx (Success)
-
- o 3xxx (Protocol Errors)
-
- o 4xxx (Transient Failures)
-
- o 5xxx (Permanent Failure)
-
- An unrecognized class (one whose first digit is not defined in this
- section) MUST be handled as a permanent failure.
-
-7.1.1. Informational
-
- Errors that fall within this category are used to inform the
- requester that a request could not be satisfied, and additional
- action is required on its part before access is granted.
-
- DIAMETER_MULTI_ROUND_AUTH 1001
-
- This informational error is returned by a Diameter server to
- inform the access device that the authentication mechanism being
- used requires multiple round trips, and a subsequent request needs
- to be issued in order for access to be granted.
-
-7.1.2. Success
-
- Errors that fall within the Success category are used to inform a
- peer that a request has been successfully completed.
-
- DIAMETER_SUCCESS 2001
-
- The request was successfully completed.
-
- DIAMETER_LIMITED_SUCCESS 2002
-
- When returned, the request was successfully completed, but
- additional processing is required by the application in order to
- provide service to the user.
-
-7.1.3. Protocol Errors
-
- Errors that fall within the Protocol Error category SHOULD be treated
- on a per-hop basis, and Diameter proxies MAY attempt to correct the
- error, if it is possible. Note that these errors MUST only be used
- in answer messages whose 'E' bit is set.
-
-
-
-
-Fajardo, et al. Standards Track [Page 90]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- DIAMETER_COMMAND_UNSUPPORTED 3001
-
- This error code is used when a Diameter entity receives a message
- with a Command Code that it does not support.
-
- DIAMETER_UNABLE_TO_DELIVER 3002
-
- This error is given when Diameter cannot deliver the message to
- the destination, either because no host within the realm
- supporting the required application was available to process the
- request or because the Destination-Host AVP was given without the
- associated Destination-Realm AVP.
-
- DIAMETER_REALM_NOT_SERVED 3003
-
- The intended realm of the request is not recognized.
-
- DIAMETER_TOO_BUSY 3004
-
- When returned, a Diameter node SHOULD attempt to send the message
- to an alternate peer. This error MUST only be used when a
- specific server is requested, and it cannot provide the requested
- service.
-
- DIAMETER_LOOP_DETECTED 3005
-
- An agent detected a loop while trying to get the message to the
- intended recipient. The message MAY be sent to an alternate peer,
- if one is available, but the peer reporting the error has
- identified a configuration problem.
-
- DIAMETER_REDIRECT_INDICATION 3006
-
- A redirect agent has determined that the request could not be
- satisfied locally, and the initiator of the request SHOULD direct
- the request directly to the server, whose contact information has
- been added to the response. When set, the Redirect-Host AVP MUST
- be present.
-
- DIAMETER_APPLICATION_UNSUPPORTED 3007
-
- A request was sent for an application that is not supported.
-
- DIAMETER_INVALID_HDR_BITS 3008
-
- A request was received whose bits in the Diameter header were set
- either to an invalid combination or to a value that is
- inconsistent with the Command Code's definition.
-
-
-
-Fajardo, et al. Standards Track [Page 91]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- DIAMETER_INVALID_AVP_BITS 3009
-
- A request was received that included an AVP whose flag bits are
- set to an unrecognized value or that is inconsistent with the
- AVP's definition.
-
- DIAMETER_UNKNOWN_PEER 3010
-
- A CER was received from an unknown peer.
-
-7.1.4. Transient Failures
-
- Errors that fall within the transient failures category are used to
- inform a peer that the request could not be satisfied at the time it
- was received but MAY be able to satisfy the request in the future.
- Note that these errors MUST be used in answer messages whose 'E' bit
- is not set.
-
- DIAMETER_AUTHENTICATION_REJECTED 4001
-
- The authentication process for the user failed, most likely due to
- an invalid password used by the user. Further attempts MUST only
- be tried after prompting the user for a new password.
-
- DIAMETER_OUT_OF_SPACE 4002
-
- A Diameter node received the accounting request but was unable to
- commit it to stable storage due to a temporary lack of space.
-
- ELECTION_LOST 4003
-
- The peer has determined that it has lost the election process and
- has therefore disconnected the transport connection.
-
-7.1.5. Permanent Failures
-
- Errors that fall within the permanent failures category are used to
- inform the peer that the request failed and should not be attempted
- again. Note that these errors SHOULD be used in answer messages
- whose 'E' bit is not set. In error conditions where it is not
- possible or efficient to compose application-specific answer grammar,
- answer messages with the 'E' bit set and which comply to the grammar
- described in Section 7.2 MAY also be used for permanent errors.
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 92]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- DIAMETER_AVP_UNSUPPORTED 5001
-
- The peer received a message that contained an AVP that is not
- recognized or supported and was marked with the 'M' (Mandatory)
- bit. A Diameter message with this error MUST contain one or more
- Failed-AVP AVPs containing the AVPs that caused the failure.
-
- DIAMETER_UNKNOWN_SESSION_ID 5002
-
- The request contained an unknown Session-Id.
-
- DIAMETER_AUTHORIZATION_REJECTED 5003
-
- A request was received for which the user could not be authorized.
- This error could occur if the service requested is not permitted
- to the user.
-
- DIAMETER_INVALID_AVP_VALUE 5004
-
- The request contained an AVP with an invalid value in its data
- portion. A Diameter message indicating this error MUST include
- the offending AVPs within a Failed-AVP AVP.
-
- DIAMETER_MISSING_AVP 5005
-
- The request did not contain an AVP that is required by the Command
- Code definition. If this value is sent in the Result-Code AVP, a
- Failed-AVP AVP SHOULD be included in the message. The Failed-AVP
- AVP MUST contain an example of the missing AVP complete with the
- Vendor-Id if applicable. The value field of the missing AVP
- should be of correct minimum length and contain zeroes.
-
- DIAMETER_RESOURCES_EXCEEDED 5006
-
- A request was received that cannot be authorized because the user
- has already expended allowed resources. An example of this error
- condition is when a user that is restricted to one dial-up PPP
- port attempts to establish a second PPP connection.
-
- DIAMETER_CONTRADICTING_AVPS 5007
-
- The Home Diameter server has detected AVPs in the request that
- contradicted each other, and it is not willing to provide service
- to the user. The Failed-AVP AVP MUST be present, which contain
- the AVPs that contradicted each other.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 93]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- DIAMETER_AVP_NOT_ALLOWED 5008
-
- A message was received with an AVP that MUST NOT be present. The
- Failed-AVP AVP MUST be included and contain a copy of the
- offending AVP.
-
- DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009
-
- A message was received that included an AVP that appeared more
- often than permitted in the message definition. The Failed-AVP
- AVP MUST be included and contain a copy of the first instance of
- the offending AVP that exceeded the maximum number of occurrences.
-
- DIAMETER_NO_COMMON_APPLICATION 5010
-
- This error is returned by a Diameter node that receives a CER
- whereby no applications are common between the CER sending peer
- and the CER receiving peer.
-
- DIAMETER_UNSUPPORTED_VERSION 5011
-
- This error is returned when a request was received, whose version
- number is unsupported.
-
- DIAMETER_UNABLE_TO_COMPLY 5012
-
- This error is returned when a request is rejected for unspecified
- reasons.
-
- DIAMETER_INVALID_BIT_IN_HEADER 5013
-
- This error is returned when a reserved bit in the Diameter header
- is set to one (1) or the bits in the Diameter header are set
- incorrectly.
-
- DIAMETER_INVALID_AVP_LENGTH 5014
-
- The request contained an AVP with an invalid length. A Diameter
- message indicating this error MUST include the offending AVPs
- within a Failed-AVP AVP. In cases where the erroneous AVP length
- value exceeds the message length or is less than the minimum AVP
- header length, it is sufficient to include the offending AVP
- header and a zero filled payload of the minimum required length
- for the payloads data type. If the AVP is a Grouped AVP, the
- Grouped AVP header with an empty payload would be sufficient to
- indicate the offending AVP. In the case where the offending AVP
- header cannot be fully decoded when the AVP length is less than
-
-
-
-
-Fajardo, et al. Standards Track [Page 94]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- the minimum AVP header length, it is sufficient to include an
- offending AVP header that is formulated by padding the incomplete
- AVP header with zero up to the minimum AVP header length.
-
- DIAMETER_INVALID_MESSAGE_LENGTH 5015
-
- This error is returned when a request is received with an invalid
- message length.
-
- DIAMETER_INVALID_AVP_BIT_COMBO 5016
-
- The request contained an AVP with which is not allowed to have the
- given value in the AVP Flags field. A Diameter message indicating
- this error MUST include the offending AVPs within a Failed-AVP
- AVP.
-
- DIAMETER_NO_COMMON_SECURITY 5017
-
- This error is returned when a CER message is received, and there
- are no common security mechanisms supported between the peers. A
- Capabilities-Exchange-Answer (CEA) message MUST be returned with
- the Result-Code AVP set to DIAMETER_NO_COMMON_SECURITY.
-
-7.2. Error Bit
-
- The 'E' (Error Bit) in the Diameter header is set when the request
- caused a protocol-related error (see Section 7.1.3). A message with
- the 'E' bit MUST NOT be sent as a response to an answer message.
- Note that a message with the 'E' bit set is still subjected to the
- processing rules defined in Section 6.2. When set, the answer
- message will not conform to the CCF specification for the command;
- instead, it and will conform to the following CCF:
-
- Message Format
-
- &lt;answer-message> ::= &lt; Diameter Header: code, ERR [, PXY] >
- 0*1&lt; Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Result-Code }
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- [ Failed-AVP ]
- [ Experimental-Result ]
- * [ Proxy-Info ]
- * [ AVP ]
-
-
-
-
-Fajardo, et al. Standards Track [Page 95]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Note that the code used in the header is the same than the one found
- in the request message, but with the 'R' bit cleared and the 'E' bit
- set. The 'P' bit in the header is set to the same value as the one
- found in the request message.
-
-7.3. Error-Message AVP
-
- The Error-Message AVP (AVP Code 281) is of type UTF8String. It MAY
- accompany a Result-Code AVP as a human-readable error message. The
- Error-Message AVP is not intended to be useful in an environment
- where error messages are processed automatically. It SHOULD NOT be
- expected that the content of this AVP be parsed by network entities.
-
-7.4. Error-Reporting-Host AVP
-
- The Error-Reporting-Host AVP (AVP Code 294) is of type
- DiameterIdentity. This AVP contains the identity of the Diameter
- host that sent the Result-Code AVP to a value other than 2001
- (Success), only if the host setting the Result-Code is different from
- the one encoded in the Origin-Host AVP. This AVP is intended to be
- used for troubleshooting purposes, and it MUST be set when the
- Result-Code AVP indicates a failure.
-
-7.5. Failed-AVP AVP
-
- The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
- debugging information in cases where a request is rejected or not
- fully processed due to erroneous information in a specific AVP. The
- value of the Result-Code AVP will provide information on the reason
- for the Failed-AVP AVP. A Diameter answer message SHOULD contain an
- instance of the Failed-AVP AVP that corresponds to the error
- indicated by the Result-Code AVP. For practical purposes, this
- Failed-AVP would typically refer to the first AVP processing error
- that a Diameter node encounters.
-
- The possible reasons for this AVP are the presence of an improperly
- constructed AVP, an unsupported or unrecognized AVP, an invalid AVP
- value, the omission of a required AVP, the presence of an explicitly
- excluded AVP (see tables in Section 10) or the presence of two or
- more occurrences of an AVP that is restricted to 0, 1, or 0-1
- occurrences.
-
- A Diameter message SHOULD contain one Failed-AVP AVP, containing the
- entire AVP that could not be processed successfully. If the failure
- reason is omission of a required AVP, an AVP with the missing AVP
- code, the missing Vendor-Id, and a zero-filled payload of the minimum
- required length for the omitted AVP will be added. If the failure
- reason is an invalid AVP length where the reported length is less
-
-
-
-Fajardo, et al. Standards Track [Page 96]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- than the minimum AVP header length or greater than the reported
- message length, a copy of the offending AVP header and a zero-filled
- payload of the minimum required length SHOULD be added.
-
- In the case where the offending AVP is embedded within a Grouped AVP,
- the Failed-AVP MAY contain the grouped AVP, which in turn contains
- the single offending AVP. The same method MAY be employed if the
- grouped AVP itself is embedded in yet another grouped AVP and so on.
- In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up
- to the single offending AVP. This enables the recipient to detect
- the location of the offending AVP when embedded in a group.
-
- AVP Format
-
- &lt;Failed-AVP> ::= &lt; AVP Header: 279 >
- 1* {AVP}
-
-7.6. Experimental-Result AVP
-
- The Experimental-Result AVP (AVP Code 297) is of type Grouped, and
- indicates whether a particular vendor-specific request was completed
- successfully or whether an error occurred. This AVP has the
- following structure:
-
- AVP Format
-
- Experimental-Result ::= &lt; AVP Header: 297 >
- { Vendor-Id }
- { Experimental-Result-Code }
-
- The Vendor-Id AVP (see Section 5.3.3) in this grouped AVP identifies
- the vendor responsible for the assignment of the result code that
- follows. All Diameter answer messages defined in vendor-specific
- applications MUST include either one Result-Code AVP or one
- Experimental-Result AVP.
-
-7.7. Experimental-Result-Code AVP
-
- The Experimental-Result-Code AVP (AVP Code 298) is of type Unsigned32
- and contains a vendor-assigned value representing the result of
- processing the request.
-
- It is recommended that vendor-specific result codes follow the same
- conventions given for the Result-Code AVP regarding the different
- types of result codes and the handling of errors (for non-2xxx
- values).
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 97]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-8. Diameter User Sessions
-
- In general, Diameter can provide two different types of services to
- applications. The first involves authentication and authorization,
- and it can optionally make use of accounting. The second only makes
- use of accounting.
-
- When a service makes use of the authentication and/or authorization
- portion of an application, and a user requests access to the network,
- the Diameter client issues an auth request to its local server. The
- auth request is defined in a service-specific Diameter application
- (e.g., NASREQ). The request contains a Session-Id AVP, which is used
- in subsequent messages (e.g., subsequent authorization, accounting,
- etc.) relating to the user's session. The Session-Id AVP is a means
- for the client and servers to correlate a Diameter message with a
- user session.
-
- When a Diameter server authorizes a user to implement network
- resources for a finite amount of time, and it is willing to extend
- the authorization via a future request, it MUST add the
- Authorization- Lifetime AVP to the answer message. The
- Authorization-Lifetime AVP defines the maximum number of seconds a
- user MAY make use of the resources before another authorization
- request is expected by the server. The Auth-Grace-Period AVP
- contains the number of seconds following the expiration of the
- Authorization-Lifetime, after which the server will release all state
- information related to the user's session. Note that if payment for
- services is expected by the serving realm from the user's home realm,
- the Authorization-Lifetime AVP, combined with the Auth-Grace-Period
- AVP, implies the maximum length of the session for which the home
- realm is willing to be fiscally responsible. Services provided past
- the expiration of the Authorization-Lifetime and Auth-Grace-Period
- AVPs are the responsibility of the access device. Of course, the
- actual cost of services rendered is clearly outside the scope of the
- protocol.
-
- An access device that does not expect to send a re-authorization or a
- session termination request to the server MAY include the Auth-
- Session-State AVP with the value set to NO_STATE_MAINTAINED as a hint
- to the server. If the server accepts the hint, it agrees that since
- no session termination message will be received once service to the
- user is terminated, it cannot maintain state for the session. If the
- answer message from the server contains a different value in the
- Auth-Session-State AVP (or the default value if the AVP is absent),
- the access device MUST follow the server's directives. Note that the
- value NO_STATE_MAINTAINED MUST NOT be set in subsequent re-
- authorization requests and answers.
-
-
-
-
-Fajardo, et al. Standards Track [Page 98]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The base protocol does not include any authorization request
- messages, since these are largely application-specific and are
- defined in a Diameter application document. However, the base
- protocol does define a set of messages that are used to terminate
- user sessions. These are used to allow servers that maintain state
- information to free resources.
-
- When a service only makes use of the accounting portion of the
- Diameter protocol, even in combination with an application, the
- Session-Id is still used to identify user sessions. However, the
- session termination messages are not used, since a session is
- signaled as being terminated by issuing an accounting stop message.
-
- Diameter may also be used for services that cannot be easily
- categorized as authentication, authorization, or accounting (e.g.,
- certain Third Generation Partnership Project Internet Multimedia
- System (3GPP IMS) interfaces). In such cases, the finite state
- machine defined in subsequent sections may not be applicable.
- Therefore, the application itself MAY need to define its own finite
- state machine. However, such application-specific state machines
- SHOULD follow the general state machine framework outlined in this
- document such as the use of Session-Id AVPs and the use of STR/STA,
- ASR/ASA messages for stateful sessions.
-
-8.1. Authorization Session State Machine
-
- This section contains a set of finite state machines, which represent
- the life cycle of Diameter sessions and which MUST be observed by all
- Diameter implementations that make use of the authentication and/or
- authorization portion of a Diameter application. The term "Service-
- Specific" below refers to a message defined in a Diameter application
- (e.g., Mobile IPv4, NASREQ).
-
- There are four different authorization session state machines
- supported in the Diameter base protocol. The first two describe a
- session in which the server is maintaining session state, indicated
- by the value of the Auth-Session-State AVP (or its absence). One
- describes the session from a client perspective, the other from a
- server perspective. The second two state machines are used when the
- server does not maintain session state. Here again, one describes
- the session from a client perspective, the other from a server
- perspective.
-
- When a session is moved to the Idle state, any resources that were
- allocated for the particular session must be released. Any event not
- listed in the state machines MUST be considered an error condition,
- and an answer, if applicable, MUST be returned to the originator of
- the message.
-
-
-
-Fajardo, et al. Standards Track [Page 99]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- In the case that an application does not support re-auth, the state
- transitions related to server-initiated re-auth, when both client and
- server sessions maintain state (e.g., Send RAR, Pending, Receive
- RAA), MAY be ignored.
-
- In the state table, the event "Failure to send X" means that the
- Diameter agent is unable to send command X to the desired
- destination. This could be due to the peer being down or due to the
- peer sending back a transient failure or temporary protocol error
- notification DIAMETER_TOO_BUSY or DIAMETER_LOOP_DETECTED in the
- Result-Code AVP of the corresponding Answer command. The event 'X
- successfully sent' is the complement of 'Failure to send X'.
-
- The following state machine is observed by a client when state is
- maintained on the server:
-
- CLIENT, STATEFUL
- State Event Action New State
- ---------------------------------------------------------------
- Idle Client or device requests Send Pending
- access service-
- specific
- auth req
-
- Idle ASR Received Send ASA Idle
- for unknown session with
- Result-Code =
- UNKNOWN_
- SESSION_ID
-
- Idle RAR Received Send RAA Idle
- for unknown session with
- Result-Code =
- UNKNOWN_
- SESSION_ID
-
- Pending Successful service-specific Grant Open
- authorization answer Access
- received with default
- Auth-Session-State value
-
- Pending Successful service-specific Sent STR Discon
- authorization answer received,
- but service not provided
-
- Pending Error processing successful Sent STR Discon
- service-specific authorization
- answer
-
-
-
-Fajardo, et al. Standards Track [Page 100]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Pending Failed service-specific Clean up Idle
- authorization answer received
-
- Open User or client device Send Open
- requests access to service service-
- specific
- auth req
-
- Open Successful service-specific Provide Open
- authorization answer received service
-
- Open Failed service-specific Discon. Idle
- authorization answer user/device
- received.
-
- Open RAR received and client will Send RAA Open
- perform subsequent re-auth with
- Result-Code =
- SUCCESS
-
- Open RAR received and client will Send RAA Idle
- not perform subsequent with
- re-auth Result-Code !=
- SUCCESS,
- Discon.
- user/device
-
- Open Session-Timeout expires on Send STR Discon
- access device
-
- Open ASR received, Send ASA Discon
- client will comply with
- with request to end the Result-Code =
- session = SUCCESS,
- Send STR.
-
- Open ASR Received, Send ASA Open
- client will not comply with
- with request to end the Result-Code !=
- session != SUCCESS
-
- Open Authorization-Lifetime + Send STR Discon
- Auth-Grace-Period expires on
- access device
-
- Discon ASR received Send ASA Discon
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 101]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Discon STA received Discon. Idle
- user/device
-
- The following state machine is observed by a server when it is
- maintaining state for the session:
-
- SERVER, STATEFUL
- State Event Action New State
- ---------------------------------------------------------------
- Idle Service-specific authorization Send Open
- request received, and successful
- user is authorized service-
- specific
- answer
-
- Idle Service-specific authorization Send Idle
- request received, and failed
- user is not authorized service-
- specific
- answer
-
- Open Service-specific authorization Send Open
- request received, and user successful
- is authorized service-
- specific
- answer
-
- Open Service-specific authorization Send Idle
- request received, and user failed
- is not authorized service-
- specific
- answer,
- Clean up
-
- Open Home server wants to confirm Send RAR Pending
- authentication and/or
- authorization of the user
-
- Pending Received RAA with a failed Clean up Idle
- Result-Code
-
- Pending Received RAA with Result-Code Update Open
- = SUCCESS session
-
- Open Home server wants to Send ASR Discon
- terminate the service
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 102]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Open Authorization-Lifetime (and Clean up Idle
- Auth-Grace-Period) expires
- on home server
-
- Open Session-Timeout expires on Clean up Idle
- home server
-
- Discon Failure to send ASR Wait, Discon
- resend ASR
-
- Discon ASR successfully sent and Clean up Idle
- ASA Received with Result-Code
-
- Not ASA Received None No Change
- Discon
-
- Any STR Received Send STA, Idle
- Clean up
-
- The following state machine is observed by a client when state is not
- maintained on the server:
-
- CLIENT, STATELESS
- State Event Action New State
- ---------------------------------------------------------------
- Idle Client or device requests Send Pending
- access service-
- specific
- auth req
-
- Pending Successful service-specific Grant Open
- authorization answer access
- received with Auth-Session-
- State set to
- NO_STATE_MAINTAINED
-
- Pending Failed service-specific Clean up Idle
- authorization answer
- received
-
- Open Session-Timeout expires on Discon. Idle
- access device user/device
-
- Open Service to user is terminated Discon. Idle
- user/device
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 103]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The following state machine is observed by a server when it is not
- maintaining state for the session:
-
- SERVER, STATELESS
- State Event Action New State
- ---------------------------------------------------------------
- Idle Service-specific authorization Send Idle
- request received, and service-
- successfully processed specific
- answer
-
-8.2. Accounting Session State Machine
-
- The following state machines MUST be supported for applications that
- have an accounting portion or that require only accounting services.
- The first state machine is to be observed by clients.
-
- See Section 9.7 for Accounting Command Codes and Section 9.8 for
- Accounting AVPs.
-
- The server side in the accounting state machine depends in some cases
- on the particular application. The Diameter base protocol defines a
- default state machine that MUST be followed by all applications that
- have not specified other state machines. This is the second state
- machine in this section described below.
-
- The default server side state machine requires the reception of
- accounting records in any order and at any time, and it does not
- place any standards requirement on the processing of these records.
- Implementations of Diameter may perform checking, ordering,
- correlation, fraud detection, and other tasks based on these records.
- AVPs may need to be inspected as a part of these tasks. The tasks
- can happen either immediately after record reception or in a post-
- processing phase. However, as these tasks are typically application
- or even policy dependent, they are not standardized by the Diameter
- specifications. Applications MAY define requirements on when to
- accept accounting records based on the used value of Accounting-
- Realtime-Required AVP, credit-limit checks, and so on.
-
- However, the Diameter base protocol defines one optional server side
- state machine that MAY be followed by applications that require
- keeping track of the session state at the accounting server. Note
- that such tracking is incompatible with the ability to sustain long
- duration connectivity problems. Therefore, the use of this state
- machine is recommended only in applications where the value of the
- Accounting-Realtime-Required AVP is DELIVER_AND_GRANT; hence,
- accounting connectivity problems are required to cause the serviced
- user to be disconnected. Otherwise, records produced by the client
-
-
-
-Fajardo, et al. Standards Track [Page 104]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- may be lost by the server, which no longer accepts them after the
- connectivity is re-established. This state machine is the third
- state machine in this section. The state machine is supervised by a
- supervision session timer Ts, whose value should be reasonably higher
- than the Acct_Interim_Interval value. Ts MAY be set to two times the
- value of the Acct_Interim_Interval so as to avoid the accounting
- session in the Diameter server to change to Idle state in case of
- short transient network failure.
-
- Any event not listed in the state machines MUST be considered as an
- error condition, and a corresponding answer, if applicable, MUST be
- returned to the originator of the message.
-
- In the state table, the event "Failure to send" means that the
- Diameter client is unable to communicate with the desired
- destination. This could be due to the peer being down, or due to the
- peer sending back a transient failure or temporary protocol error
- notification DIAMETER_OUT_OF_SPACE, DIAMETER_TOO_BUSY, or
- DIAMETER_LOOP_DETECTED in the Result-Code AVP of the Accounting
- Answer command.
-
- The event "Failed answer" means that the Diameter client received a
- non-transient failure notification in the Accounting Answer command.
-
- Note that the action "Disconnect user/dev" MUST also have an effect
- on the authorization session state table, e.g., cause the STR message
- to be sent, if the given application has both authentication/
- authorization and accounting portions.
-
- The states PendingS, PendingI, PendingL, PendingE, and PendingB stand
- for pending states to wait for an answer to an accounting request
- related to a Start, Interim, Stop, Event, or buffered record,
- respectively.
-
- CLIENT, ACCOUNTING
- State Event Action New State
- ---------------------------------------------------------------
- Idle Client or device requests Send PendingS
- access accounting
- start req.
-
- Idle Client or device requests Send PendingE
- a one-time service accounting
- event req
-
- Idle Records in storage Send PendingB
- record
-
-
-
-
-Fajardo, et al. Standards Track [Page 105]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- PendingS Successful accounting Open
- start answer received
-
- PendingS Failure to send and buffer Store Open
- space available and real time Start
- not equal to DELIVER_AND_GRANT Record
-
- PendingS Failure to send and no buffer Open
- space available and real time
- equal to GRANT_AND_LOSE
-
- PendingS Failure to send and no Disconnect Idle
- buffer space available and user/dev
- real time not equal to
- GRANT_AND_LOSE
-
- PendingS Failed accounting start answer Open
- received and real time equal
- to GRANT_AND_LOSE
-
- PendingS Failed accounting start answer Disconnect Idle
- received and real time not user/dev
- equal to GRANT_AND_LOSE
-
- PendingS User service terminated Store PendingS
- stop
- record
-
- Open Interim interval elapses Send PendingI
- accounting
- interim
- record
-
- Open User service terminated Send PendingL
- accounting
- stop req.
-
- PendingI Successful accounting interim Open
- answer received
-
- PendingI Failure to send and (buffer Store Open
- space available or old interim
- record can be overwritten) record
- and real time not equal to
- DELIVER_AND_GRANT
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 106]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- PendingI Failure to send and no buffer Open
- space available and real time
- equal to GRANT_AND_LOSE
-
- PendingI Failure to send and no Disconnect Idle
- buffer space available and user/dev
- real time not equal to
- GRANT_AND_LOSE
-
- PendingI Failed accounting interim Open
- answer received and real time
- equal to GRANT_AND_LOSE
-
- PendingI Failed accounting interim Disconnect Idle
- answer received and user/dev
- real time not equal to
- GRANT_AND_LOSE
-
- PendingI User service terminated Store PendingI
- stop
- record
- PendingE Successful accounting Idle
- event answer received
-
- PendingE Failure to send and buffer Store Idle
- space available event
- record
-
- PendingE Failure to send and no buffer Idle
- space available
-
- PendingE Failed accounting event answer Idle
- received
-
- PendingB Successful accounting answer Delete Idle
- received record
-
- PendingB Failure to send Idle
-
- PendingB Failed accounting answer Delete Idle
- received record
-
- PendingL Successful accounting Idle
- stop answer received
-
- PendingL Failure to send and buffer Store Idle
- space available stop
- record
-
-
-
-Fajardo, et al. Standards Track [Page 107]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- PendingL Failure to send and no buffer Idle
- space available
-
- PendingL Failed accounting stop answer Idle
- received
-
-
- SERVER, STATELESS ACCOUNTING
- State Event Action New State
- ---------------------------------------------------------------
-
- Idle Accounting start request Send Idle
- received and successfully accounting
- processed. start
- answer
-
- Idle Accounting event request Send Idle
- received and successfully accounting
- processed. event
- answer
-
- Idle Interim record received Send Idle
- and successfully processed. accounting
- interim
- answer
-
- Idle Accounting stop request Send Idle
- received and successfully accounting
- processed stop answer
-
- Idle Accounting request received; Send Idle
- no space left to store accounting
- records answer;
- Result-Code =
- OUT_OF_
- SPACE
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 108]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- SERVER, STATEFUL ACCOUNTING
- State Event Action New State
- ---------------------------------------------------------------
-
- Idle Accounting start request Send Open
- received and successfully accounting
- processed. start
- answer;
- Start Ts
-
- Idle Accounting event request Send Idle
- received and successfully accounting
- processed. event
- answer
- Idle Accounting request received; Send Idle
- no space left to store accounting
- records answer;
- Result-Code =
- OUT_OF_
- SPACE
-
- Open Interim record received Send Open
- and successfully processed. accounting
- interim
- answer;
- Restart Ts
-
- Open Accounting stop request Send Idle
- received and successfully accounting
- processed stop answer;
- Stop Ts
-
- Open Accounting request received; Send Idle
- no space left to store accounting
- records answer;
- Result-Code =
- OUT_OF_
- SPACE;
- Stop Ts
-
- Open Session supervision timer Ts Stop Ts Idle
- expired
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 109]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-8.3. Server-Initiated Re-Auth
-
- A Diameter server may initiate a re-authentication and/or re-
- authorization service for a particular session by issuing a Re-Auth-
- Request (RAR).
-
- For example, for prepaid services, the Diameter server that
- originally authorized a session may need some confirmation that the
- user is still using the services.
-
- An access device that receives an RAR message with the Session-Id
- equal to a currently active session MUST initiate a re-auth towards
- the user, if the service supports this particular feature. Each
- Diameter application MUST state whether server-initiated re-auth is
- supported, since some applications do not allow access devices to
- prompt the user for re-auth.
-
-8.3.1. Re-Auth-Request
-
- The Re-Auth-Request (RAR), indicated by the Command Code set to 258
- and the message flags' 'R' bit set, may be sent by any server to the
- access device that is providing session service, to request that the
- user be re-authenticated and/or re-authorized.
-
-
- Message Format
-
- &lt;RAR> ::= &lt; Diameter Header: 258, REQ, PXY >
- &lt; Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Destination-Host }
- { Auth-Application-Id }
- { Re-Auth-Request-Type }
- [ User-Name ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
-8.3.2. Re-Auth-Answer
-
- The Re-Auth-Answer (RAA), indicated by the Command Code set to 258
- and the message flags' 'R' bit clear, is sent in response to the RAR.
- The Result-Code AVP MUST be present, and it indicates the disposition
- of the request.
-
-
-
-
-Fajardo, et al. Standards Track [Page 110]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- A successful RAA message MUST be followed by an application-specific
- authentication and/or authorization message.
-
- Message Format
-
- &lt;RAA> ::= &lt; Diameter Header: 258, PXY >
- &lt; Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- [ Failed-AVP ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
-8.4. Session Termination
-
- It is necessary for a Diameter server that authorized a session, for
- which it is maintaining state, to be notified when that session is no
- longer active, both for tracking purposes as well as to allow
- stateful agents to release any resources that they may have provided
- for the user's session. For sessions whose state is not being
- maintained, this section is not used.
-
- When a user session that required Diameter authorization terminates,
- the access device that provided the service MUST issue a Session-
- Termination-Request (STR) message to the Diameter server that
- authorized the service, to notify it that the session is no longer
- active. An STR MUST be issued when a user session terminates for any
- reason, including user logoff, expiration of Session-Timeout,
- administrative action, termination upon receipt of an Abort-Session-
- Request (see below), orderly shutdown of the access device, etc.
-
- The access device also MUST issue an STR for a session that was
- authorized but never actually started. This could occur, for
- example, due to a sudden resource shortage in the access device, or
- because the access device is unwilling to provide the type of service
- requested in the authorization, or because the access device does not
- support a mandatory AVP returned in the authorization, etc.
-
- It is also possible that a session that was authorized is never
- actually started due to action of a proxy. For example, a proxy may
-
-
-
-Fajardo, et al. Standards Track [Page 111]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- modify an authorization answer, converting the result from success to
- failure, prior to forwarding the message to the access device. If
- the answer did not contain an Auth-Session-State AVP with the value
- NO_STATE_MAINTAINED, a proxy that causes an authorized session not to
- be started MUST issue an STR to the Diameter server that authorized
- the session, since the access device has no way of knowing that the
- session had been authorized.
-
- A Diameter server that receives an STR message MUST clean up
- resources (e.g., session state) associated with the Session-Id
- specified in the STR and return a Session-Termination-Answer.
-
- A Diameter server also MUST clean up resources when the Session-
- Timeout expires, or when the Authorization-Lifetime and the Auth-
- Grace-Period AVPs expire without receipt of a re-authorization
- request, regardless of whether an STR for that session is received.
- The access device is not expected to provide service beyond the
- expiration of these timers; thus, expiration of either of these
- timers implies that the access device may have unexpectedly shut
- down.
-
-8.4.1. Session-Termination-Request
-
- The Session-Termination-Request (STR), indicated by the Command Code
- set to 275 and the Command Flags' 'R' bit set, is sent by a Diameter
- client or by a Diameter proxy to inform the Diameter server that an
- authenticated and/or authorized session is being terminated.
-
- Message Format
-
- &lt;STR> ::= &lt; Diameter Header: 275, REQ, PXY >
- &lt; Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Auth-Application-Id }
- { Termination-Cause }
- [ User-Name ]
- [ Destination-Host ]
- * [ Class ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 112]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-8.4.2. Session-Termination-Answer
-
- The Session-Termination-Answer (STA), indicated by the Command Code
- set to 275 and the message flags' 'R' bit clear, is sent by the
- Diameter server to acknowledge the notification that the session has
- been terminated. The Result-Code AVP MUST be present, and it MAY
- contain an indication that an error occurred while servicing the STR.
-
- Upon sending or receipt of the STA, the Diameter server MUST release
- all resources for the session indicated by the Session-Id AVP. Any
- intermediate server in the Proxy-Chain MAY also release any
- resources, if necessary.
-
- Message Format
-
- &lt;STA> ::= &lt; Diameter Header: 275, PXY >
- &lt; Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- * [ Class ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- [ Failed-AVP ]
- [ Origin-State-Id ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
-8.5. Aborting a Session
-
- A Diameter server may request that the access device stop providing
- service for a particular session by issuing an Abort-Session-Request
- (ASR).
-
- For example, the Diameter server that originally authorized the
- session may be required to cause that session to be stopped for lack
- of credit or other reasons that were not anticipated when the session
- was first authorized.
-
- An access device that receives an ASR with Session-ID equal to a
- currently active session MAY stop the session. Whether the access
- device stops the session or not is implementation and/or
- configuration dependent. For example, an access device may honor
- ASRs from certain agents only. In any case, the access device MUST
-
-
-
-Fajardo, et al. Standards Track [Page 113]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- respond with an Abort-Session-Answer, including a Result-Code AVP to
- indicate what action it took.
-
-8.5.1. Abort-Session-Request
-
- The Abort-Session-Request (ASR), indicated by the Command Code set to
- 274 and the message flags' 'R' bit set, may be sent by any Diameter
- server or any Diameter proxy to the access device that is providing
- session service, to request that the session identified by the
- Session-Id be stopped.
-
- Message Format
-
- &lt;ASR> ::= &lt; Diameter Header: 274, REQ, PXY >
- &lt; Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Destination-Host }
- { Auth-Application-Id }
- [ User-Name ]
- [ Origin-State-Id ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
-8.5.2. Abort-Session-Answer
-
- The Abort-Session-Answer (ASA), indicated by the Command Code set to
- 274 and the message flags' 'R' bit clear, is sent in response to the
- ASR. The Result-Code AVP MUST be present and indicates the
- disposition of the request.
-
- If the session identified by Session-Id in the ASR was successfully
- terminated, the Result-Code is set to DIAMETER_SUCCESS. If the
- session is not currently active, the Result-Code is set to
- DIAMETER_UNKNOWN_SESSION_ID. If the access device does not stop the
- session for any other reason, the Result-Code is set to
- DIAMETER_UNABLE_TO_COMPLY.
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 114]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Message Format
-
- &lt;ASA> ::= &lt; Diameter Header: 274, PXY >
- &lt; Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- [ User-Name ]
- [ Origin-State-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- [ Failed-AVP ]
- * [ Redirect-Host ]
- [ Redirect-Host-Usage ]
- [ Redirect-Max-Cache-Time ]
- * [ Proxy-Info ]
- * [ AVP ]
-
-8.6. Inferring Session Termination from Origin-State-Id
-
- The Origin-State-Id is used to allow detection of terminated sessions
- for which no STR would have been issued, due to unanticipated
- shutdown of an access device.
-
- A Diameter client or access device increments the value of the
- Origin-State-Id every time it is started or powered up. The new
- Origin-State-Id is then sent in the CER/CEA message immediately upon
- connection to the server. The Diameter server receiving the new
- Origin-State-Id can determine whether the sending Diameter client had
- abruptly shut down by comparing the old value of the Origin-State-Id
- it has kept for that specific client is less than the new value and
- whether it has un-terminated sessions originating from that client.
-
- An access device can also include the Origin-State-Id in request
- messages other than the CER if there are relays or proxies in between
- the access device and the server. In this case, however, the server
- cannot discover that the access device has been restarted unless and
- until it receives a new request from it. Therefore, this mechanism
- is more opportunistic across proxies and relays.
-
- The Diameter server may assume that all sessions that were active
- prior to detection of a client restart have been terminated. The
- Diameter server MAY clean up all session state associated with such
- lost sessions, and it MAY also issue STRs for all such lost sessions
- that were authorized on upstream servers, to allow session state to
- be cleaned up globally.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 115]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-8.7. Auth-Request-Type AVP
-
- The Auth-Request-Type AVP (AVP Code 274) is of type Enumerated and is
- included in application-specific auth requests to inform the peers
- whether a user is to be authenticated only, authorized only, or both.
- Note any value other than both MAY cause RADIUS interoperability
- issues. The following values are defined:
-
- AUTHENTICATE_ONLY 1
-
- The request being sent is for authentication only, and it MUST
- contain the relevant application-specific authentication AVPs that
- are needed by the Diameter server to authenticate the user.
-
- AUTHORIZE_ONLY 2
-
- The request being sent is for authorization only, and it MUST
- contain the application-specific authorization AVPs that are
- necessary to identify the service being requested/offered.
-
- AUTHORIZE_AUTHENTICATE 3
-
- The request contains a request for both authentication and
- authorization. The request MUST include both the relevant
- application-specific authentication information and authorization
- information necessary to identify the service being requested/
- offered.
-
-8.8. Session-Id AVP
-
- The Session-Id AVP (AVP Code 263) is of type UTF8String and is used
- to identify a specific session (see Section 8). All messages
- pertaining to a specific session MUST include only one Session-Id
- AVP, and the same value MUST be used throughout the life of a
- session. When present, the Session-Id SHOULD appear immediately
- following the Diameter header (see Section 3).
-
- The Session-Id MUST be globally and eternally unique, as it is meant
- to uniquely identify a user session without reference to any other
- information, and it may be needed to correlate historical
- authentication information with accounting information. The
- Session-Id includes a mandatory portion and an implementation-defined
- portion; a recommended format for the implementation-defined portion
- is outlined below.
-
- The Session-Id MUST begin with the sender's identity encoded in the
- DiameterIdentity type (see Section 4.3.1). The remainder of the
- Session-Id is delimited by a ";" character, and it MAY be any
-
-
-
-Fajardo, et al. Standards Track [Page 116]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- sequence that the client can guarantee to be eternally unique;
- however, the following format is recommended, (square brackets []
- indicate an optional element):
-
- &lt;DiameterIdentity>;&lt;high 32 bits>;&lt;low 32 bits>[;&lt;optional value>]
-
- &lt;high 32 bits> and &lt;low 32 bits> are decimal representations of the
- high and low 32 bits of a monotonically increasing 64-bit value. The
- 64-bit value is rendered in two part to simplify formatting by 32-bit
- processors. At startup, the high 32 bits of the 64-bit value MAY be
- initialized to the time in NTP format [RFC5905], and the low 32 bits
- MAY be initialized to zero. This will for practical purposes
- eliminate the possibility of overlapping Session-Ids after a reboot,
- assuming the reboot process takes longer than a second.
- Alternatively, an implementation MAY keep track of the increasing
- value in non-volatile memory.
-
-
- &lt;optional value> is implementation specific, but it may include a
- modem's device Id, a Layer 2 address, timestamp, etc.
-
- Example, in which there is no optional value:
-
- accesspoint7.example.com;1876543210;523
-
- Example, in which there is an optional value:
-
- accesspoint7.example.com;1876543210;523;[email protected]
-
- The Session-Id is created by the Diameter application initiating the
- session, which, in most cases, is done by the client. Note that a
- Session-Id MAY be used for both the authentication, authorization,
- and accounting commands of a given application.
-
-8.9. Authorization-Lifetime AVP
-
- The Authorization-Lifetime AVP (AVP Code 291) is of type Unsigned32
- and contains the maximum number of seconds of service to be provided
- to the user before the user is to be re-authenticated and/or re-
- authorized. Care should be taken when the Authorization-Lifetime
- value is determined, since a low, non-zero value could create
- significant Diameter traffic, which could congest both the network
- and the agents.
-
- A value of zero (0) means that immediate re-auth is necessary by the
- access device. The absence of this AVP, or a value of all ones
- (meaning all bits in the 32-bit field are set to one) means no re-
- auth is expected.
-
-
-
-Fajardo, et al. Standards Track [Page 117]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- If both this AVP and the Session-Timeout AVP are present in a
- message, the value of the latter MUST NOT be smaller than the
- Authorization-Lifetime AVP.
-
- An Authorization-Lifetime AVP MAY be present in re-authorization
- messages, and it contains the number of seconds the user is
- authorized to receive service from the time the re-auth answer
- message is received by the access device.
-
- This AVP MAY be provided by the client as a hint of the maximum
- lifetime that it is willing to accept. The server MUST return a
- value that is equal to, or smaller than, the one provided by the
- client.
-
-8.10. Auth-Grace-Period AVP
-
- The Auth-Grace-Period AVP (AVP Code 276) is of type Unsigned32 and
- contains the number of seconds the Diameter server will wait
- following the expiration of the Authorization-Lifetime AVP before
- cleaning up resources for the session.
-
-8.11. Auth-Session-State AVP
-
- The Auth-Session-State AVP (AVP Code 277) is of type Enumerated and
- specifies whether state is maintained for a particular session. The
- client MAY include this AVP in requests as a hint to the server, but
- the value in the server's answer message is binding. The following
- values are supported:
-
- STATE_MAINTAINED 0
-
- This value is used to specify that session state is being
- maintained, and the access device MUST issue a session termination
- message when service to the user is terminated. This is the
- default value.
-
- NO_STATE_MAINTAINED 1
-
- This value is used to specify that no session termination messages
- will be sent by the access device upon expiration of the
- Authorization-Lifetime.
-
-8.12. Re-Auth-Request-Type AVP
-
- The Re-Auth-Request-Type AVP (AVP Code 285) is of type Enumerated and
- is included in application-specific auth answers to inform the client
- of the action expected upon expiration of the Authorization-Lifetime.
-
-
-
-
-Fajardo, et al. Standards Track [Page 118]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- If the answer message contains an Authorization-Lifetime AVP with a
- positive value, the Re-Auth-Request-Type AVP MUST be present in an
- answer message. The following values are defined:
-
- AUTHORIZE_ONLY 0
-
- An authorization only re-auth is expected upon expiration of the
- Authorization-Lifetime. This is the default value if the AVP is
- not present in answer messages that include the Authorization-
- Lifetime.
-
- AUTHORIZE_AUTHENTICATE 1
-
- An authentication and authorization re-auth is expected upon
- expiration of the Authorization-Lifetime.
-
-8.13. Session-Timeout AVP
-
- The Session-Timeout AVP (AVP Code 27) [RFC2865] is of type Unsigned32
- and contains the maximum number of seconds of service to be provided
- to the user before termination of the session. When both the
- Session-Timeout and the Authorization-Lifetime AVPs are present in an
- answer message, the former MUST be equal to or greater than the value
- of the latter.
-
- A session that terminates on an access device due to the expiration
- of the Session-Timeout MUST cause an STR to be issued, unless both
- the access device and the home server had previously agreed that no
- session termination messages would be sent (see Section 8).
-
- A Session-Timeout AVP MAY be present in a re-authorization answer
- message, and it contains the remaining number of seconds from the
- beginning of the re-auth.
-
- A value of zero, or the absence of this AVP, means that this session
- has an unlimited number of seconds before termination.
-
- This AVP MAY be provided by the client as a hint of the maximum
- timeout that it is willing to accept. However, the server MAY return
- a value that is equal to, or smaller than, the one provided by the
- client.
-
-8.14. User-Name AVP
-
- The User-Name AVP (AVP Code 1) [RFC2865] is of type UTF8String, which
- contains the User-Name, in a format consistent with the NAI
- specification [RFC4282].
-
-
-
-
-Fajardo, et al. Standards Track [Page 119]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-8.15. Termination-Cause AVP
-
- The Termination-Cause AVP (AVP Code 295) is of type Enumerated, and
- is used to indicate the reason why a session was terminated on the
- access device. The currently assigned values for this AVP can be
- found in the IANA registry for Termination-Cause AVP Values
- [IANATCV].
-
-8.16. Origin-State-Id AVP
-
- The Origin-State-Id AVP (AVP Code 278), of type Unsigned32, is a
- monotonically increasing value that is advanced whenever a Diameter
- entity restarts with loss of previous state, for example, upon
- reboot. Origin-State-Id MAY be included in any Diameter message,
- including CER.
-
- A Diameter entity issuing this AVP MUST create a higher value for
- this AVP each time its state is reset. A Diameter entity MAY set
- Origin-State-Id to the time of startup, or it MAY use an incrementing
- counter retained in non-volatile memory across restarts.
-
- The Origin-State-Id, if present, MUST reflect the state of the entity
- indicated by Origin-Host. If a proxy modifies Origin-Host, it MUST
- either remove Origin-State-Id or modify it appropriately as well.
- Typically, Origin-State-Id is used by an access device that always
- starts up with no active sessions; that is, any session active prior
- to restart will have been lost. By including Origin-State-Id in a
- message, it allows other Diameter entities to infer that sessions
- associated with a lower Origin-State-Id are no longer active. If an
- access device does not intend for such inferences to be made, it MUST
- either not include Origin-State-Id in any message or set its value to
- 0.
-
-8.17. Session-Binding AVP
-
- The Session-Binding AVP (AVP Code 270) is of type Unsigned32, and it
- MAY be present in application-specific authorization answer messages.
- If present, this AVP MAY inform the Diameter client that all future
- application-specific re-auth and Session-Termination-Request messages
- for this session MUST be sent to the same authorization server.
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 120]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- This field is a bit mask, and the following bits have been defined:
-
- RE_AUTH 1
-
- When set, future re-auth messages for this session MUST NOT
- include the Destination-Host AVP. When cleared, the default
- value, the Destination-Host AVP MUST be present in all re-auth
- messages for this session.
-
- STR 2
-
- When set, the STR message for this session MUST NOT include the
- Destination-Host AVP. When cleared, the default value, the
- Destination-Host AVP MUST be present in the STR message for this
- session.
-
- ACCOUNTING 4
-
- When set, all accounting messages for this session MUST NOT
- include the Destination-Host AVP. When cleared, the default
- value, the Destination-Host AVP, if known, MUST be present in all
- accounting messages for this session.
-
-8.18. Session-Server-Failover AVP
-
- The Session-Server-Failover AVP (AVP Code 271) is of type Enumerated
- and MAY be present in application-specific authorization answer
- messages that either do not include the Session-Binding AVP or
- include the Session-Binding AVP with any of the bits set to a zero
- value. If present, this AVP MAY inform the Diameter client that if a
- re-auth or STR message fails due to a delivery problem, the Diameter
- client SHOULD issue a subsequent message without the Destination-Host
- AVP. When absent, the default value is REFUSE_SERVICE.
-
- The following values are supported:
-
- REFUSE_SERVICE 0
-
- If either the re-auth or the STR message delivery fails, terminate
- service with the user and do not attempt any subsequent attempts.
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 121]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- TRY_AGAIN 1
-
- If either the re-auth or the STR message delivery fails, resend
- the failed message without the Destination-Host AVP present.
-
- ALLOW_SERVICE 2
-
- If re-auth message delivery fails, assume that re-authorization
- succeeded. If STR message delivery fails, terminate the session.
-
- TRY_AGAIN_ALLOW_SERVICE 3
-
- If either the re-auth or the STR message delivery fails, resend
- the failed message without the Destination-Host AVP present. If
- the second delivery fails for re-auth, assume re-authorization
- succeeded. If the second delivery fails for STR, terminate the
- session.
-
-8.19. Multi-Round-Time-Out AVP
-
- The Multi-Round-Time-Out AVP (AVP Code 272) is of type Unsigned32 and
- SHOULD be present in application-specific authorization answer
- messages whose Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH.
- This AVP contains the maximum number of seconds that the access
- device MUST provide the user in responding to an authentication
- request.
-
-8.20. Class AVP
-
- The Class AVP (AVP Code 25) is of type OctetString and is used by
- Diameter servers to return state information to the access device.
- When one or more Class AVPs are present in application-specific
- authorization answer messages, they MUST be present in subsequent re-
- authorization, session termination and accounting messages. Class
- AVPs found in a re-authorization answer message override the ones
- found in any previous authorization answer message. Diameter server
- implementations SHOULD NOT return Class AVPs that require more than
- 4096 bytes of storage on the Diameter client. A Diameter client that
- receives Class AVPs whose size exceeds local available storage MUST
- terminate the session.
-
-8.21. Event-Timestamp AVP
-
- The Event-Timestamp (AVP Code 55) is of type Time and MAY be included
- in an Accounting-Request and Accounting-Answer messages to record the
- time that the reported event occurred, in seconds since January 1,
- 1900 00:00 UTC.
-
-
-
-
-Fajardo, et al. Standards Track [Page 122]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-9. Accounting
-
- This accounting protocol is based on a server directed model with
- capabilities for real-time delivery of accounting information.
- Several fault resilience methods [RFC2975] have been built into the
- protocol in order minimize loss of accounting data in various fault
- situations and under different assumptions about the capabilities of
- the used devices.
-
-9.1. Server Directed Model
-
- The server directed model means that the device generating the
- accounting data gets information from either the authorization server
- (if contacted) or the accounting server regarding the way accounting
- data shall be forwarded. This information includes accounting record
- timeliness requirements.
-
- As discussed in [RFC2975], real-time transfer of accounting records
- is a requirement, such as the need to perform credit-limit checks and
- fraud detection. Note that batch accounting is not a requirement,
- and is therefore not supported by Diameter. Should batched
- accounting be required in the future, a new Diameter application will
- need to be created, or it could be handled using another protocol.
- Note, however, that even if at the Diameter layer, accounting
- requests are processed one by one; transport protocols used under
- Diameter typically batch several requests in the same packet under
- heavy traffic conditions. This may be sufficient for many
- applications.
-
- The authorization server (chain) directs the selection of proper
- transfer strategy, based on its knowledge of the user and
- relationships of roaming partnerships. The server (or agents) uses
- the Acct-Interim-Interval and Accounting-Realtime-Required AVPs to
- control the operation of the Diameter peer operating as a client.
- The Acct-Interim-Interval AVP, when present, instructs the Diameter
- node acting as a client to produce accounting records continuously
- even during a session. Accounting-Realtime-Required AVP is used to
- control the behavior of the client when the transfer of accounting
- records from the Diameter client is delayed or unsuccessful.
-
- The Diameter accounting server MAY override the interim interval or
- the real-time requirements by including the Acct-Interim-Interval or
- Accounting-Realtime-Required AVP in the Accounting-Answer message.
- When one of these AVPs is present, the latest value received SHOULD
- be used in further accounting activities for the same session.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 123]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-9.2. Protocol Messages
-
- A Diameter node that receives a successful authentication and/or
- authorization message from the Diameter server SHOULD collect
- accounting information for the session. The Accounting-Request
- message is used to transmit the accounting information to the
- Diameter server, which MUST reply with the Accounting-Answer message
- to confirm reception. The Accounting-Answer message includes the
- Result-Code AVP, which MAY indicate that an error was present in the
- accounting message. The value of the Accounting-Realtime-Required
- AVP received earlier for the session in question may indicate that
- the user's session has to be terminated when a rejected Accounting-
- Request message was received.
-
-9.3. Accounting Application Extension and Requirements
-
- Each Diameter application (e.g., NASREQ, Mobile IP) SHOULD define its
- service-specific AVPs that MUST be present in the Accounting-Request
- message in a section titled "Accounting AVPs". The application MUST
- assume that the AVPs described in this document will be present in
- all Accounting messages, so only their respective service-specific
- AVPs need to be defined in that section.
-
- Applications have the option of using one or both of the following
- accounting application extension models:
-
- Split Accounting Service
-
- The accounting message will carry the Application Id of the
- Diameter base accounting application (see Section 2.4).
- Accounting messages may be routed to Diameter nodes other than the
- corresponding Diameter application. These nodes might be
- centralized accounting servers that provide accounting service for
- multiple different Diameter applications. These nodes MUST
- advertise the Diameter base accounting Application Id during
- capabilities exchange.
-
- Coupled Accounting Service
-
- The accounting message will carry the Application Id of the
- application that is using it. The application itself will process
- the received accounting records or forward them to an accounting
- server. There is no accounting application advertisement required
- during capabilities exchange, and the accounting messages will be
- routed the same way as any of the other application messages.
-
- In cases where an application does not define its own accounting
- service, it is preferred that the split accounting model be used.
-
-
-
-Fajardo, et al. Standards Track [Page 124]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-9.4. Fault Resilience
-
- Diameter base protocol mechanisms are used to overcome small message
- loss and network faults of a temporary nature.
-
- Diameter peers acting as clients MUST implement the use of failover
- to guard against server failures and certain network failures.
- Diameter peers acting as agents or related off-line processing
- systems MUST detect duplicate accounting records caused by the
- sending of the same record to several servers and duplication of
- messages in transit. This detection MUST be based on the inspection
- of the Session-Id and Accounting-Record-Number AVP pairs. Appendix C
- discusses duplicate detection needs and implementation issues.
-
- Diameter clients MAY have non-volatile memory for the safe storage of
- accounting records over reboots or extended network failures, network
- partitions, and server failures. If such memory is available, the
- client SHOULD store new accounting records there as soon as the
- records are created and until a positive acknowledgement of their
- reception from the Diameter server has been received. Upon a reboot,
- the client MUST start sending the records in the non-volatile memory
- to the accounting server with the appropriate modifications in
- termination cause, session length, and other relevant information in
- the records.
-
- A further application of this protocol may include AVPs to control
- the maximum number of accounting records that may be stored in the
- Diameter client without committing them to the non-volatile memory or
- transferring them to the Diameter server.
-
- The client SHOULD NOT remove the accounting data from any of its
- memory areas before the correct Accounting-Answer has been received.
- The client MAY remove the oldest, undelivered, or as yet
- unacknowledged accounting data if it runs out of resources such as
- memory. It is an implementation-dependent matter for the client to
- accept new sessions under this condition.
-
-9.5. Accounting Records
-
- In all accounting records, the Session-Id AVP MUST be present; the
- User-Name AVP MUST be present if it is available to the Diameter
- client.
-
- Different types of accounting records are sent depending on the
- actual type of accounted service and the authorization server's
- directions for interim accounting. If the accounted service is a
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 125]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- one-time event, meaning that the start and stop of the event are
- simultaneous, then the Accounting-Record-Type AVP MUST be present and
- set to the value EVENT_RECORD.
-
- If the accounted service is of a measurable length, then the AVP MUST
- use the values START_RECORD, STOP_RECORD, and possibly,
- INTERIM_RECORD. If the authorization server has not directed interim
- accounting to be enabled for the session, two accounting records MUST
- be generated for each service of type session. When the initial
- Accounting-Request for a given session is sent, the Accounting-
- Record-Type AVP MUST be set to the value START_RECORD. When the last
- Accounting-Request is sent, the value MUST be STOP_RECORD.
-
- If the authorization server has directed interim accounting to be
- enabled, the Diameter client MUST produce additional records between
- the START_RECORD and STOP_RECORD, marked INTERIM_RECORD. The
- production of these records is directed by Acct-Interim-Interval as
- well as any re-authentication or re-authorization of the session.
- The Diameter client MUST overwrite any previous interim accounting
- records that are locally stored for delivery, if a new record is
- being generated for the same session. This ensures that only one
- pending interim record can exist on an access device for any given
- session.
-
- A particular value of Accounting-Sub-Session-Id MUST appear only in
- one sequence of accounting records from a Diameter client, except for
- the purposes of retransmission. The one sequence that is sent MUST
- be either one record with Accounting-Record-Type AVP set to the value
- EVENT_RECORD or several records starting with one having the value
- START_RECORD, followed by zero or more INTERIM_RECORDs and a single
- STOP_RECORD. A particular Diameter application specification MUST
- define the type of sequences that MUST be used.
-
-9.6. Correlation of Accounting Records
-
- If an application uses accounting messages, it can correlate
- accounting records with a specific application session by using the
- Session-Id of the particular application session in the accounting
- messages. Accounting messages MAY also use a different Session-Id
- from that of the application sessions, in which case, other session-
- related information is needed to perform correlation.
-
- In cases where an application requires multiple accounting sub-
- sessions, an Accounting-Sub-Session-Id AVP is used to differentiate
- each sub-session. The Session-Id would remain constant for all sub-
- sessions and is used to correlate all the sub-sessions to a
- particular application session. Note that receiving a STOP_RECORD
-
-
-
-
-Fajardo, et al. Standards Track [Page 126]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- with no Accounting-Sub-Session-Id AVP when sub-sessions were
- originally used in the START_RECORD messages implies that all sub-
- sessions are terminated.
-
- There are also cases where an application needs to correlate multiple
- application sessions into a single accounting record; the accounting
- record may span multiple different Diameter applications and sessions
- used by the same user at a given time. In such cases, the Acct-
- Multi-Session-Id AVP is used. The Acct-Multi-Session-Id AVP SHOULD
- be signaled by the server to the access device (typically, during
- authorization) when it determines that a request belongs to an
- existing session. The access device MUST then include the Acct-
- Multi-Session-Id AVP in all subsequent accounting messages.
-
- The Acct-Multi-Session-Id AVP MAY include the value of the original
- Session-Id. Its contents are implementation specific, but the MUST
- be globally unique across other Acct-Multi-Session-Ids and MUST NOT
- change during the life of a session.
-
- A Diameter application document MUST define the exact concept of a
- session that is being accounted, and it MAY define the concept of a
- multi-session. For instance, the NASREQ DIAMETER application treats
- a single PPP connection to a Network Access Server as one session and
- a set of Multilink PPP sessions as one multi-session.
-
-9.7. Accounting Command Codes
-
- This section defines Command Code values that MUST be supported by
- all Diameter implementations that provide accounting services.
-
-9.7.1. Accounting-Request
-
- The Accounting-Request (ACR) command, indicated by the Command Code
- field set to 271 and the Command Flags' 'R' bit set, is sent by a
- Diameter node, acting as a client, in order to exchange accounting
- information with a peer.
-
- In addition to the AVPs listed below, Accounting-Request messages
- SHOULD include service-specific accounting AVPs.
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 127]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Message Format
-
- &lt;ACR> ::= &lt; Diameter Header: 271, REQ, PXY >
- &lt; Session-Id >
- { Origin-Host }
- { Origin-Realm }
- { Destination-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Destination-Host ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ Route-Record ]
- * [ AVP ]
-
-9.7.2. Accounting-Answer
-
- The Accounting-Answer (ACA) command, indicated by the Command Code
- field set to 271 and the Command Flags' 'R' bit cleared, is used to
- acknowledge an Accounting-Request command. The Accounting-Answer
- command contains the same Session-Id as the corresponding request.
-
- Only the target Diameter server, known as the home Diameter server,
- SHOULD respond with the Accounting-Answer command.
-
- In addition to the AVPs listed below, Accounting-Answer messages
- SHOULD include service-specific accounting AVPs.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 128]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Message Format
-
- &lt;ACA> ::= &lt; Diameter Header: 271, PXY >
- &lt; Session-Id >
- { Result-Code }
- { Origin-Host }
- { Origin-Realm }
- { Accounting-Record-Type }
- { Accounting-Record-Number }
- [ Acct-Application-Id ]
- [ Vendor-Specific-Application-Id ]
- [ User-Name ]
- [ Accounting-Sub-Session-Id ]
- [ Acct-Session-Id ]
- [ Acct-Multi-Session-Id ]
- [ Error-Message ]
- [ Error-Reporting-Host ]
- [ Failed-AVP ]
- [ Acct-Interim-Interval ]
- [ Accounting-Realtime-Required ]
- [ Origin-State-Id ]
- [ Event-Timestamp ]
- * [ Proxy-Info ]
- * [ AVP ]
-
-9.8. Accounting AVPs
-
- This section contains AVPs that describe accounting usage information
- related to a specific session.
-
-9.8.1. Accounting-Record-Type AVP
-
- The Accounting-Record-Type AVP (AVP Code 480) is of type Enumerated
- and contains the type of accounting record being sent. The following
- values are currently defined for the Accounting-Record-Type AVP:
-
- EVENT_RECORD 1
-
- An Accounting Event Record is used to indicate that a one-time
- event has occurred (meaning that the start and end of the event
- are simultaneous). This record contains all information relevant
- to the service, and it is the only record of the service.
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 129]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- START_RECORD 2
-
- Accounting Start, Interim, and Stop Records are used to indicate
- that a service of a measurable length has been given. An
- Accounting Start Record is used to initiate an accounting session
- and contains accounting information that is relevant to the
- initiation of the session.
-
- INTERIM_RECORD 3
-
- An Interim Accounting Record contains cumulative accounting
- information for an existing accounting session. Interim
- Accounting Records SHOULD be sent every time a re-authentication
- or re-authorization occurs. Further, additional interim record
- triggers MAY be defined by application-specific Diameter
- applications. The selection of whether to use INTERIM_RECORD
- records is done by the Acct-Interim-Interval AVP.
-
- STOP_RECORD 4
-
- An Accounting Stop Record is sent to terminate an accounting
- session and contains cumulative accounting information relevant to
- the existing session.
-
-9.8.2. Acct-Interim-Interval AVP
-
- The Acct-Interim-Interval AVP (AVP Code 85) is of type Unsigned32 and
- is sent from the Diameter home authorization server to the Diameter
- client. The client uses information in this AVP to decide how and
- when to produce accounting records. With different values in this
- AVP, service sessions can result in one, two, or two+N accounting
- records, based on the needs of the home organization. The following
- accounting record production behavior is directed by the inclusion of
- this AVP:
-
- 1. The omission of the Acct-Interim-Interval AVP or its inclusion
- with Value field set to 0 means that EVENT_RECORD, START_RECORD,
- and STOP_RECORD are produced, as appropriate for the service.
-
- 2. The inclusion of the AVP with Value field set to a non-zero value
- means that INTERIM_RECORD records MUST be produced between the
- START_RECORD and STOP_RECORD records. The Value field of this
- AVP is the nominal interval between these records in seconds.
- The Diameter node that originates the accounting information,
- known as the client, MUST produce the first INTERIM_RECORD record
- roughly at the time when this nominal interval has elapsed from
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 130]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- the START_RECORD, the next one again as the interval has elapsed
- once more, and so on until the session ends and a STOP_RECORD
- record is produced.
-
- The client MUST ensure that the interim record production times
- are randomized so that large accounting message storms are not
- created either among records or around a common service start
- time.
-
-9.8.3. Accounting-Record-Number AVP
-
- The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
- and identifies this record within one session. As Session-Id AVPs
- are globally unique, the combination of Session-Id and Accounting-
- Record-Number AVPs is also globally unique and can be used in
- matching accounting records with confirmations. An easy way to
- produce unique numbers is to set the value to 0 for records of type
- EVENT_RECORD and START_RECORD and set the value to 1 for the first
- INTERIM_RECORD, 2 for the second, and so on until the value for
- STOP_RECORD is one more than for the last INTERIM_RECORD.
-
-9.8.4. Acct-Session-Id AVP
-
- The Acct-Session-Id AVP (AVP Code 44) is of type OctetString is only
- used when RADIUS/Diameter translation occurs. This AVP contains the
- contents of the RADIUS Acct-Session-Id attribute.
-
-9.8.5. Acct-Multi-Session-Id AVP
-
- The Acct-Multi-Session-Id AVP (AVP Code 50) is of type UTF8String,
- following the format specified in Section 8.8. The Acct-Multi-
- Session-Id AVP is used to link multiple related accounting sessions,
- where each session would have a unique Session-Id but the same Acct-
- Multi-Session-Id AVP. This AVP MAY be returned by the Diameter
- server in an authorization answer, and it MUST be used in all
- accounting messages for the given session.
-
-9.8.6. Accounting-Sub-Session-Id AVP
-
- The Accounting-Sub-Session-Id AVP (AVP Code 287) is of type
- Unsigned64 and contains the accounting sub-session identifier. The
- combination of the Session-Id and this AVP MUST be unique per sub-
- session, and the value of this AVP MUST be monotonically increased by
- one for all new sub-sessions. The absence of this AVP implies no
- sub-sessions are in use, with the exception of an Accounting-Request
- whose Accounting-Record-Type is set to STOP_RECORD. A STOP_RECORD
- message with no Accounting-Sub-Session-Id AVP present will signal the
- termination of all sub-sessions for a given Session-Id.
-
-
-
-Fajardo, et al. Standards Track [Page 131]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-9.8.7. Accounting-Realtime-Required AVP
-
- The Accounting-Realtime-Required AVP (AVP Code 483) is of type
- Enumerated and is sent from the Diameter home authorization server to
- the Diameter client or in the Accounting-Answer from the accounting
- server. The client uses information in this AVP to decide what to do
- if the sending of accounting records to the accounting server has
- been temporarily prevented due to, for instance, a network problem.
-
- DELIVER_AND_GRANT 1
-
- The AVP with Value field set to DELIVER_AND_GRANT means that the
- service MUST only be granted as long as there is a connection to
- an accounting server. Note that the set of alternative accounting
- servers are treated as one server in this sense. Having to move
- the accounting record stream to a backup server is not a reason to
- discontinue the service to the user.
-
- GRANT_AND_STORE 2
-
- The AVP with Value field set to GRANT_AND_STORE means that service
- SHOULD be granted if there is a connection, or as long as records
- can still be stored as described in Section 9.4.
-
- This is the default behavior if the AVP isn't included in the
- reply from the authorization server.
-
- GRANT_AND_LOSE 3
-
- The AVP with Value field set to GRANT_AND_LOSE means that service
- SHOULD be granted even if the records cannot be delivered or
- stored.
-
-10. AVP Occurrence Tables
-
- The following tables present the AVPs defined in this document and
- specify in which Diameter messages they MAY or MAY NOT be present.
- AVPs that occur only inside a Grouped AVP are not shown in these
- tables.
-
- The tables use the following symbols:
-
- 0 The AVP MUST NOT be present in the message.
-
- 0+ Zero or more instances of the AVP MAY be present in the
- message.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 132]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- 0-1 Zero or one instance of the AVP MAY be present in the message.
- It is considered an error if there are more than one instance
- of the AVP.
-
- 1 One instance of the AVP MUST be present in the message.
-
- 1+ At least one instance of the AVP MUST be present in the
- message.
-
-10.1. Base Protocol Command AVP Table
-
- The table in this section is limited to the non-Accounting Command
- Codes defined in this specification.
-
- +-----------------------------------------------+
- | Command Code |
- +---+---+---+---+---+---+---+---+---+---+---+---+
- Attribute Name |CER|CEA|DPR|DPA|DWR|DWA|RAR|RAA|ASR|ASA|STR|STA|
- --------------------+---+---+---+---+---+---+---+---+---+---+---+---+
- Acct-Interim- |0 |0 |0 |0 |0 |0 |0-1|0 |0 |0 |0 |0 |
- Interval | | | | | | | | | | | | |
- Accounting-Realtime-|0 |0 |0 |0 |0 |0 |0-1|0 |0 |0 |0 |0 |
- Required | | | | | | | | | | | | |
- Acct-Application-Id |0+ |0+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Auth-Application-Id |0+ |0+ |0 |0 |0 |0 |1 |0 |1 |0 |1 |0 |
- Auth-Grace-Period |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Auth-Request-Type |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Auth-Session-State |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Authorization- |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Lifetime | | | | | | | | | | | | |
- Class |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0+ |0+ |
- Destination-Host |0 |0 |0 |0 |0 |0 |1 |0 |1 |0 |0-1|0 |
- Destination-Realm |0 |0 |0 |0 |0 |0 |1 |0 |1 |0 |1 |0 |
- Disconnect-Cause |0 |0 |1 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Error-Message |0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|
- Error-Reporting-Host|0 |0 |0 |0 |0 |0 |0 |0-1|0 |0-1|0 |0-1|
- Failed-AVP |0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|0 |0-1|
- Firmware-Revision |0-1|0-1|0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Host-IP-Address |1+ |1+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Inband-Security-Id |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Multi-Round-Time-Out|0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 133]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Origin-Host |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |
- Origin-Realm |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |1 |
- Origin-State-Id |0-1|0-1|0 |0 |0-1|0-1|0-1|0-1|0-1|0-1|0-1|0-1|
- Product-Name |1 |1 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Proxy-Info |0 |0 |0 |0 |0 |0 |0+ |0+ |0+ |0+ |0+ |0+ |
- Redirect-Host |0 |0 |0 |0 |0 |0 |0 |0+ |0 |0+ |0 |0+ |
- Redirect-Host-Usage |0 |0 |0 |0 |0 |0 |0 |0-1|0 |0-1|0 |0-1|
- Redirect-Max-Cache- |0 |0 |0 |0 |0 |0 |0 |0-1|0 |0-1|0 |0-1|
- Time | | | | | | | | | | | | |
- Result-Code |0 |1 |0 |1 |0 |1 |0 |1 |0 |1 |0 |1 |
- Re-Auth-Request-Type|0 |0 |0 |0 |0 |0 |1 |0 |0 |0 |0 |0 |
- Route-Record |0 |0 |0 |0 |0 |0 |0+ |0 |0+ |0 |0+ |0 |
- Session-Binding |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Session-Id |0 |0 |0 |0 |0 |0 |1 |1 |1 |1 |1 |1 |
- Session-Server- |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Failover | | | | | | | | | | | | |
- Session-Timeout |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Supported-Vendor-Id |0+ |0+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Termination-Cause |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |1 |0 |
- User-Name |0 |0 |0 |0 |0 |0 |0-1|0-1|0-1|0-1|0-1|0-1|
- Vendor-Id |1 |1 |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Vendor-Specific- |0+ |0+ |0 |0 |0 |0 |0 |0 |0 |0 |0 |0 |
- Application-Id | | | | | | | | | | | | |
- --------------------+---+---+---+---+---+---+---+---+---+---+---+---+
-
-10.2. Accounting AVP Table
-
- The table in this section is used to represent which AVPs defined in
- this document are to be present in the Accounting messages. These
- AVP occurrence requirements are guidelines, which may be expanded,
- and/or overridden by application-specific requirements in the
- Diameter applications documents.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 134]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- +-----------+
- | Command |
- | Code |
- +-----+-----+
- Attribute Name | ACR | ACA |
- ------------------------------+-----+-----+
- Acct-Interim-Interval | 0-1 | 0-1 |
- Acct-Multi-Session-Id | 0-1 | 0-1 |
- Accounting-Record-Number | 1 | 1 |
- Accounting-Record-Type | 1 | 1 |
- Acct-Session-Id | 0-1 | 0-1 |
- Accounting-Sub-Session-Id | 0-1 | 0-1 |
- Accounting-Realtime-Required | 0-1 | 0-1 |
- Acct-Application-Id | 0-1 | 0-1 |
- Auth-Application-Id | 0 | 0 |
- Class | 0+ | 0+ |
- Destination-Host | 0-1 | 0 |
- Destination-Realm | 1 | 0 |
- Error-Reporting-Host | 0 | 0+ |
- Event-Timestamp | 0-1 | 0-1 |
- Failed-AVP | 0 | 0-1 |
- Origin-Host | 1 | 1 |
- Origin-Realm | 1 | 1 |
- Proxy-Info | 0+ | 0+ |
- Route-Record | 0+ | 0 |
- Result-Code | 0 | 1 |
- Session-Id | 1 | 1 |
- Termination-Cause | 0 | 0 |
- User-Name | 0-1 | 0-1 |
- Vendor-Specific-Application-Id| 0-1 | 0-1 |
- ------------------------------+-----+-----+
-
-11. IANA Considerations
-
- This section provides guidance to the Internet Assigned Numbers
- Authority (IANA) regarding registration of values related to the
- Diameter protocol, in accordance with [RFC5226]. Existing IANA
- registries and assignments put in place by RFC 3588 remain the same
- unless explicitly updated or deprecated in this section.
-
-11.1. AVP Header
-
- As defined in Section 4, the AVP header contains three fields that
- require IANA namespace management: the AVP Code, Vendor-ID, and Flags
- fields.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 135]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-11.1.1. AVP Codes
-
- There are multiple namespaces. Vendors can have their own AVP Codes
- namespace that will be identified by their Vendor-ID (also known as
- Enterprise-Number), and they control the assignments of their vendor-
- specific AVP Codes within their own namespace. The absence of a
- Vendor-ID or a Vendor-ID value of zero (0) identifies the IETF AVP
- Codes namespace, which is under IANA control. The AVP Codes and
- sometimes possible values in an AVP are controlled and maintained by
- IANA. AVP Code 0 is not used. AVP Codes 1-255 are managed
- separately as RADIUS Attribute Types. Where a Vendor-Specific AVP is
- implemented by more than one vendor, allocation of global AVPs should
- be encouraged instead.
-
- AVPs may be allocated following Expert Review (by a Designated
- Expert) with Specification Required [RFC5226]. A block allocation
- (release of more than three AVPs at a time for a given purpose)
- requires IETF Review [RFC5226].
-
-11.1.2. AVP Flags
-
- Section 4.1 describes the existing AVP Flags. The remaining bits can
- only be assigned via a Standards Action [RFC5226].
-
-11.2. Diameter Header
-
-11.2.1. Command Codes
-
- For the Diameter header, the Command Code namespace allocation has
- changed. The new allocation rules are as follows:
-
- The Command Code values 256 - 8,388,607 (0x100 to 0x7fffff) are
- for permanent, standard commands, allocated by IETF Review
- [RFC5226].
-
- The values 8,388,608 - 16,777,213 (0x800000 - 0xfffffd) are
- reserved for vendor-specific Command Codes, to be allocated on a
- First Come, First Served basis by IANA [RFC5226]. The request to
- IANA for a Vendor-Specific Command Code SHOULD include a reference
- to a publicly available specification that documents the command
- in sufficient detail to aid in interoperability between
- independent implementations. If the specification cannot be made
- publicly available, the request for a vendor-specific Command Code
- MUST include the contact information of persons and/or entities
- responsible for authoring and maintaining the command.
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 136]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- The values 16,777,214 and 16,777,215 (hexadecimal values 0xfffffe
- - 0xffffff) are reserved for experimental commands. As these
- codes are only for experimental and testing purposes, no guarantee
- is made for interoperability between Diameter peers using
- experimental commands.
-
-11.2.2. Command Flags
-
- Section 3 describes the existing Command Flags field. The remaining
- bits can only be assigned via a Standards Action [RFC5226].
-
-11.3. AVP Values
-
- For AVP values, the Experimental-Result-Code AVP value allocation has
- been added; see Section 11.3.1. The old AVP value allocation rule,
- IETF Consensus, has been updated to IETF Review as per [RFC5226], and
- affected AVPs are listed as reminders.
-
-11.3.1. Experimental-Result-Code AVP
-
- Values for this AVP are purely local to the indicated vendor, and no
- IANA registry is maintained for them.
-
-11.3.2. Result-Code AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.3. Accounting-Record-Type AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.4. Termination-Cause AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.5. Redirect-Host-Usage AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.6. Session-Server-Failover AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.7. Session-Binding AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 137]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-11.3.8. Disconnect-Cause AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.9. Auth-Request-Type AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.10. Auth-Session-State AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.11. Re-Auth-Request-Type AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.12. Accounting-Realtime-Required AVP Values
-
- New values are available for assignment via IETF Review [RFC5226].
-
-11.3.13. Inband-Security-Id AVP (code 299)
-
- The use of this AVP has been deprecated.
-
-11.4. _diameters Service Name and Port Number Registration
-
- IANA has registered the "_diameters" service name and assigned port
- numbers for TLS/TCP and DTLS/SCTP according to the guidelines given
- in [RFC6335].
-
- Service Name: _diameters
-
- Transport Protocols: TCP, SCTP
-
- Assignee: IESG &lt;[email protected]>
-
- Contact: IETF Chair &lt;[email protected]>
-
- Description: Diameter over TLS/TCP and DTLS/SCTP
-
- Reference: RFC 6733
-
- Port Number: 5868, from the User Range
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 138]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-11.5. SCTP Payload Protocol Identifiers
-
- Two SCTP payload protocol identifiers have been registered in the
- SCTP Payload Protocol Identifiers registry:
-
-
- Value | SCTP Payload Protocol Identifier
- -------|-----------------------------------
- 46 | Diameter in a SCTP DATA chunk
- 47 | Diameter in a DTLS/SCTP DATA chunk
-
-
-11.6. S-NAPTR Parameters
-
- The following tag has been registered in the S-NAPTR Application
- Protocol Tags registry:
-
- Tag | Protocol
- -------------------|---------
- diameter.dtls.sctp | DTLS/SCTP
-
-12. Diameter Protocol-Related Configurable Parameters
-
- This section contains the configurable parameters that are found
- throughout this document:
-
- Diameter Peer
-
- A Diameter entity MAY communicate with peers that are statically
- configured. A statically configured Diameter peer would require
- that either the IP address or the fully qualified domain name
- (FQDN) be supplied, which would then be used to resolve through
- DNS.
-
- Routing Table
-
- A Diameter proxy server routes messages based on the realm portion
- of a Network Access Identifier (NAI). The server MUST have a
- table of Realm Names, and the address of the peer to which the
- message must be forwarded. The routing table MAY also include a
- "default route", which is typically used for all messages that
- cannot be locally processed.
-
- Tc timer
-
- The Tc timer controls the frequency that transport connection
- attempts are done to a peer with whom no active transport
- connection exists. The recommended value is 30 seconds.
-
-
-
-Fajardo, et al. Standards Track [Page 139]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-13. Security Considerations
-
- The Diameter base protocol messages SHOULD be secured by using TLS
- [RFC5246] or DTLS/SCTP [RFC6083]. Additional security mechanisms
- such as IPsec [RFC4301] MAY also be deployed to secure connections
- between peers. However, all Diameter base protocol implementations
- MUST support the use of TLS/TCP and DTLS/SCTP, and the Diameter
- protocol MUST NOT be used without one of TLS, DTLS, or IPsec.
-
- If a Diameter connection is to be protected via TLS/TCP and DTLS/SCTP
- or IPsec, then TLS/TCP and DTLS/SCTP or IPsec/IKE SHOULD begin prior
- to any Diameter message exchange. All security parameters for TLS/
- TCP and DTLS/SCTP or IPsec are configured independent of the Diameter
- protocol. All Diameter messages will be sent through the TLS/TCP and
- DTLS/SCTP or IPsec connection after a successful setup.
-
- For TLS/TCP and DTLS/SCTP connections to be established in the open
- state, the CER/CEA exchange MUST include an Inband-Security-ID AVP
- with a value of TLS/TCP and DTLS/SCTP. The TLS/TCP and DTLS/SCTP
- handshake will begin when both ends successfully reach the open
- state, after completion of the CER/CEA exchange. If the TLS/TCP and
- DTLS/SCTP handshake is successful, all further messages will be sent
- via TLS/TCP and DTLS/SCTP. If the handshake fails, both ends MUST
- move to the closed state. See Section 13.1 for more details.
-
-13.1. TLS/TCP and DTLS/SCTP Usage
-
- Diameter nodes using TLS/TCP and DTLS/SCTP for security MUST mutually
- authenticate as part of TLS/TCP and DTLS/SCTP session establishment.
- In order to ensure mutual authentication, the Diameter node acting as
- the TLS/TCP and DTLS/SCTP server MUST request a certificate from the
- Diameter node acting as TLS/TCP and DTLS/SCTP client, and the
- Diameter node acting as the TLS/TCP and DTLS/SCTP client MUST be
- prepared to supply a certificate on request.
-
- Diameter nodes MUST be able to negotiate the following TLS/TCP and
- DTLS/SCTP cipher suites:
-
- TLS_RSA_WITH_RC4_128_MD5
- TLS_RSA_WITH_RC4_128_SHA
- TLS_RSA_WITH_3DES_EDE_CBC_SHA
-
- Diameter nodes SHOULD be able to negotiate the following TLS/TCP and
- DTLS/SCTP cipher suite:
-
- TLS_RSA_WITH_AES_128_CBC_SHA
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 140]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- Note that it is quite possible that support for the
- TLS_RSA_WITH_AES_128_CBC_SHA cipher suite will be REQUIRED at some
- future date. Diameter nodes MAY negotiate other TLS/TCP and DTLS/
- SCTP cipher suites.
-
- If public key certificates are used for Diameter security (for
- example, with TLS), the value of the expiration times in the routing
- and peer tables MUST NOT be greater than the expiry time in the
- relevant certificates.
-
-13.2. Peer-to-Peer Considerations
-
- As with any peer-to-peer protocol, proper configuration of the trust
- model within a Diameter peer is essential to security. When
- certificates are used, it is necessary to configure the root
- certificate authorities trusted by the Diameter peer. These root CAs
- are likely to be unique to Diameter usage and distinct from the root
- CAs that might be trusted for other purposes such as Web browsing.
- In general, it is expected that those root CAs will be configured so
- as to reflect the business relationships between the organization
- hosting the Diameter peer and other organizations. As a result, a
- Diameter peer will typically not be configured to allow connectivity
- with any arbitrary peer. With certificate authentication, Diameter
- peers may not be known beforehand and therefore peer discovery may be
- required.
-
-13.3. AVP Considerations
-
- Diameter AVPs often contain security-sensitive data; for example,
- user passwords and location data, network addresses and cryptographic
- keys. The following AVPs defined in this document are considered to
- be security-sensitive:
-
- o Acct-Interim-Interval
-
- o Accounting-Realtime-Required
-
- o Acct-Multi-Session-Id
-
- o Accounting-Record-Number
-
- o Accounting-Record-Type
-
- o Accounting-Session-Id
-
- o Accounting-Sub-Session-Id
-
- o Class
-
-
-
-Fajardo, et al. Standards Track [Page 141]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- o Session-Id
-
- o Session-Binding
-
- o Session-Server-Failover
-
- o User-Name
-
- Diameter messages containing these or any other AVPs considered to be
- security-sensitive MUST only be sent protected via mutually
- authenticated TLS or IPsec. In addition, those messages MUST NOT be
- sent via intermediate nodes unless there is end-to-end security
- between the originator and recipient or the originator has locally
- trusted configuration that indicates that end-to-end security is not
- needed. For example, end-to-end security may not be required in the
- case where an intermediary node is known to be operated as part of
- the same administrative domain as the endpoints so that an ability to
- successfully compromise the intermediary would imply a high
- probability of being able to compromise the endpoints as well. Note
- that no end-to-end security mechanism is specified in this document.
-
-14. References
-
-14.1. Normative References
-
- [FLOATPOINT]
- Institute of Electrical and Electronics Engineers, "IEEE
- Standard for Binary Floating-Point Arithmetic, ANSI/IEEE
- Standard 754-1985", August 1985.
-
- [IANAADFAM]
- IANA, "Address Family Numbers",
- &lt;http://www.iana.org/assignments/address-family-numbers>.
-
- [RFC0791] Postel, J., "Internet Protocol", STD 5, RFC 791,
- September 1981.
-
- [RFC0793] Postel, J., "Transmission Control Protocol", STD 7,
- RFC 793, September 1981.
-
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
- Requirement Levels", BCP 14, RFC 2119, March 1997.
-
- [RFC3492] Costello, A., "Punycode: A Bootstring encoding of Unicode
- for Internationalized Domain Names in Applications
- (IDNA)", RFC 3492, March 2003.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 142]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- [RFC3539] Aboba, B. and J. Wood, "Authentication, Authorization and
- Accounting (AAA) Transport Profile", RFC 3539, June 2003.
-
- [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
- 10646", STD 63, RFC 3629, November 2003.
-
- [RFC3958] Daigle, L. and A. Newton, "Domain-Based Application
- Service Location Using SRV RRs and the Dynamic Delegation
- Discovery Service (DDDS)", RFC 3958, January 2005.
-
- [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
- Resource Identifier (URI): Generic Syntax", STD 66,
- RFC 3986, January 2005.
-
- [RFC4004] Calhoun, P., Johansson, T., Perkins, C., Hiller, T., and
- P. McCann, "Diameter Mobile IPv4 Application", RFC 4004,
- August 2005.
-
- [RFC4005] Calhoun, P., Zorn, G., Spence, D., and D. Mitton,
- "Diameter Network Access Server Application", RFC 4005,
- August 2005.
-
- [RFC4006] Hakala, H., Mattila, L., Koskinen, J-P., Stura, M., and J.
- Loughney, "Diameter Credit-Control Application", RFC 4006,
- August 2005.
-
- [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
- Requirements for Security", BCP 106, RFC 4086, June 2005.
-
- [RFC4282] Aboba, B., Beadles, M., Arkko, J., and P. Eronen, "The
- Network Access Identifier", RFC 4282, December 2005.
-
- [RFC4291] Hinden, R. and S. Deering, "IP Version 6 Addressing
- Architecture", RFC 4291, February 2006.
-
- [RFC4960] Stewart, R., "Stream Control Transmission Protocol",
- RFC 4960, September 2007.
-
- [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
- IANA Considerations Section in RFCs", BCP 26, RFC 5226,
- May 2008.
-
- [RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
- Specifications: ABNF", STD 68, RFC 5234, January 2008.
-
- [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security
- (TLS) Protocol Version 1.2", RFC 5246, August 2008.
-
-
-
-
-Fajardo, et al. Standards Track [Page 143]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S.,
- Housley, R., and W. Polk, "Internet X.509 Public Key
- Infrastructure Certificate and Certificate Revocation List
- (CRL) Profile", RFC 5280, May 2008.
-
- [RFC5729] Korhonen, J., Jones, M., Morand, L., and T. Tsou,
- "Clarifications on the Routing of Diameter Requests Based
- on the Username and the Realm", RFC 5729, December 2009.
-
- [RFC5890] Klensin, J., "Internationalized Domain Names for
- Applications (IDNA): Definitions and Document Framework",
- RFC 5890, August 2010.
-
- [RFC5891] Klensin, J., "Internationalized Domain Names in
- Applications (IDNA): Protocol", RFC 5891, August 2010.
-
- [RFC6083] Tuexen, M., Seggelmann, R., and E. Rescorla, "Datagram
- Transport Layer Security (DTLS) for Stream Control
- Transmission Protocol (SCTP)", RFC 6083, January 2011.
-
- [RFC6347] Rescorla, E. and N. Modadugu, "Datagram Transport Layer
- Security Version 1.2", RFC 6347, January 2012.
-
- [RFC6408] Jones, M., Korhonen, J., and L. Morand, "Diameter
- Straightforward-Naming Authority Pointer (S-NAPTR) Usage",
- RFC 6408, November 2011.
-
-14.2. Informative References
-
- [ENTERPRISE] IANA, "SMI Network Management Private Enterprise
- Codes",
- &lt;http://www.iana.org/assignments/enterprise-numbers>.
-
- [IANATCV] IANA, "Termination-Cause AVP Values (code 295)",
- &lt;http://www.iana.org/assignments/aaa-parameters/
- aaa-parameters.xml#aaa-parameters-16>.
-
- [RFC1492] Finseth, C., "An Access Control Protocol, Sometimes
- Called TACACS", RFC 1492, July 1993.
-
- [RFC1661] Simpson, W., "The Point-to-Point Protocol (PPP)",
- STD 51, RFC 1661, July 1994.
-
- [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC:
- Keyed-Hashing for Message Authentication", RFC 2104,
- February 1997.
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 144]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR
- for specifying the location of services (DNS SRV)",
- RFC 2782, February 2000.
-
- [RFC2865] Rigney, C., Willens, S., Rubens, A., and W. Simpson,
- "Remote Authentication Dial In User Service (RADIUS)",
- RFC 2865, June 2000.
-
- [RFC2866] Rigney, C., "RADIUS Accounting", RFC 2866, June 2000.
-
- [RFC2869] Rigney, C., Willats, W., and P. Calhoun, "RADIUS
- Extensions", RFC 2869, June 2000.
-
- [RFC2881] Mitton, D. and M. Beadles, "Network Access Server
- Requirements Next Generation (NASREQNG) NAS Model",
- RFC 2881, July 2000.
-
- [RFC2975] Aboba, B., Arkko, J., and D. Harrington, "Introduction
- to Accounting Management", RFC 2975, October 2000.
-
- [RFC2989] Aboba, B., Calhoun, P., Glass, S., Hiller, T., McCann,
- P., Shiino, H., Walsh, P., Zorn, G., Dommety, G.,
- Perkins, C., Patil, B., Mitton, D., Manning, S.,
- Beadles, M., Chen, X., Sivalingham, S., Hameed, A.,
- Munson, M., Jacobs, S., Lim, B., Hirschman, B., Hsu,
- R., Koo, H., Lipford, M., Campbell, E., Xu, Y., Baba,
- S., and E. Jaques, "Criteria for Evaluating AAA
- Protocols for Network Access", RFC 2989, November 2000.
-
- [RFC3162] Aboba, B., Zorn, G., and D. Mitton, "RADIUS and IPv6",
- RFC 3162, August 2001.
-
- [RFC3748] Aboba, B., Blunk, L., Vollbrecht, J., Carlson, J., and
- H. Levkowetz, "Extensible Authentication Protocol
- (EAP)", RFC 3748, June 2004.
-
- [RFC4301] Kent, S. and K. Seo, "Security Architecture for the
- Internet Protocol", RFC 4301, December 2005.
-
- [RFC4690] Klensin, J., Faltstrom, P., Karp, C., and IAB, "Review
- and Recommendations for Internationalized Domain Names
- (IDNs)", RFC 4690, September 2006.
-
- [RFC5176] Chiba, M., Dommety, G., Eklund, M., Mitton, D., and B.
- Aboba, "Dynamic Authorization Extensions to Remote
- Authentication Dial In User Service (RADIUS)",
- RFC 5176, January 2008.
-
-
-
-
-Fajardo, et al. Standards Track [Page 145]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- [RFC5461] Gont, F., "TCP's Reaction to Soft Errors", RFC 5461,
- February 2009.
-
- [RFC5905] Mills, D., Martin, J., Burbank, J., and W. Kasch,
- "Network Time Protocol Version 4: Protocol and
- Algorithms Specification", RFC 5905, June 2010.
-
- [RFC5927] Gont, F., "ICMP Attacks against TCP", RFC 5927,
- July 2010.
-
- [RFC6335] Cotton, M., Eggert, L., Touch, J., Westerlund, M., and
- S. Cheshire, "Internet Assigned Numbers Authority
- (IANA) Procedures for the Management of the Service
- Name and Transport Protocol Port Number Registry",
- BCP 165, RFC 6335, August 2011.
-
- [RFC6737] Kang, J. and G. Zorn, "The Diameter Capabilities Update
- Application", RFC 6737, October 2012.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Fajardo, et al. Standards Track [Page 146]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
-Appendix A. Acknowledgements
-
-A.1. This Document
-
- The authors would like to thank the following people that have
- provided proposals and contributions to this document:
-
- To Vishnu Ram and Satendra Gera for their contributions on
- capabilities updates, predictive loop avoidance, as well as many
- other technical proposals. To Tolga Asveren for his insights and
- contributions on almost all of the proposed solutions incorporated
- into this document. To Timothy Smith for helping on the capabilities
- Update and other topics. To Tony Zhang for providing fixes to
- loopholes on composing Failed-AVPs as well as many other issues and
- topics. To Jan Nordqvist for clearly stating the usage of
- Application Ids. To Anders Kristensen for providing needed technical
- opinions. To David Frascone for providing invaluable review of the
- document. To Mark Jones for providing clarifying text on vendor
- command codes and other vendor-specific indicators. To Victor
- Pascual and Sebastien Decugis for new text and recommendations on
- SCTP/DTLS. To Jouni Korhonen for taking over the editing task and
- resolving last bits from versions 27 through 29.
-
- Special thanks to the Diameter extensibility design team, which
- helped resolve the tricky question of mandatory AVPs and ABNF
- semantics. The members of this team are as follows:
-
- Avi Lior, Jari Arkko, Glen Zorn, Lionel Morand, Mark Jones, Tolga
- Asveren, Jouni Korhonen, and Glenn McGregor.
-
- Special thanks also to people who have provided invaluable comments
- and inputs especially in resolving controversial issues:
-
- Glen Zorn, Yoshihiro Ohba, Marco Stura, Stephen Farrel, Pete Resnick,
- Peter Saint-Andre, Robert Sparks, Krishna Prasad, Sean Turner, Barry
- Leiba, and Pasi Eronen.
-
- Finally, we would like to thank the original authors of this
- document:
-
- Pat Calhoun, John Loughney, Jari Arkko, Erik Guttman, and Glen Zorn.
-
- Their invaluable knowledge and experience has given us a robust and
- flexible AAA protocol that many people have seen great value in
- adopting. We greatly appreciate their support and stewardship for
- the continued improvements of Diameter as a protocol. We would also
- like to extend our gratitude to folks aside from the authors who have
-
-
-
-
-Fajardo, et al. Standards Track [Page 147]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- assisted and contributed to the original version of this document.
- Their efforts significantly contributed to the success of Diameter.
-
-A.2. RFC 3588
-
- The authors would like to thank Nenad Trifunovic, Tony Johansson and
- Pankaj Patel for their participation in the pre-IETF Document Reading
- Party. Allison Mankin, Jonathan Wood, and Bernard Aboba provided
- invaluable assistance in working out transport issues and this was
- also the case with Steven Bellovin in the security area.
-
- Paul Funk and David Mitton were instrumental in getting the Peer
- State Machine correct, and our deep thanks go to them for their time.
-
- Text in this document was also provided by Paul Funk, Mark Eklund,
- Mark Jones, and Dave Spence. Jacques Caron provided many great
- comments as a result of a thorough review of the spec.
-
- The authors would also like to acknowledge the following people for
- their contribution in the development of the Diameter protocol:
-
- Allan C. Rubens, Haseeb Akhtar, William Bulley, Stephen Farrell,
- David Frascone, Daniel C. Fox, Lol Grant, Ignacio Goyret, Nancy
- Greene, Peter Heitman, Fredrik Johansson, Mark Jones, Martin Julien,
- Bob Kopacz, Paul Krumviede, Fergal Ladley, Ryan Moats, Victor Muslin,
- Kenneth Peirce, John Schnizlein, Sumit Vakil, John R. Vollbrecht, and
- Jeff Weisberg.
-
- Finally, Pat Calhoun would like to thank Sun Microsystems since most
- of the effort put into this document was done while he was in their
- employ.
-
-Appendix B. S-NAPTR Example
-
- As an example, consider a client that wishes to resolve aaa:
- ex1.example.com. The client performs a NAPTR query for that domain,
- and the following NAPTR records are returned:
-
- ;; order pref flags service regexp replacement
- IN NAPTR 50 50 "s" "aaa:diameter.tls.tcp" ""
- _diameter._tls.ex1.example.com
- IN NAPTR 100 50 "s" "aaa:diameter.tcp" ""
- _aaa._tcp.ex1.example.com
- IN NAPTR 150 50 "s" "aaa:diameter.sctp" ""
- _diameter._sctp.ex1.example.com
-
- This indicates that the server supports TLS, TCP, and SCTP in that
- order. If the client supports TLS, TLS will be used, targeted to a
-
-
-
-Fajardo, et al. Standards Track [Page 148]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- host determined by an SRV lookup of _diameter._tls.ex1.example.com.
- That lookup would return:
-
- ;; Priority Weight Port Target
- IN SRV 0 1 5060 server1.ex1.example.com
- IN SRV 0 2 5060 server2.ex1.example.com
-
- As an alternative example, a client that wishes to resolve aaa:
- ex2.example.com. The client performs a NAPTR query for that domain,
- and the following NAPTR records are returned:
-
- ;; order pref flags service regexp replacement
- IN NAPTR 150 50 "a" "aaa:diameter.tls.tcp" ""
- server1.ex2.example.com
- IN NAPTR 150 50 "a" "aaa:diameter.tls.tcp" ""
- server2.ex2.example.com
-
- This indicates that the server supports TCP available at the returned
- host names.
-
-Appendix C. Duplicate Detection
-
- As described in Section 9.4, accounting record duplicate detection is
- based on session identifiers. Duplicates can appear for various
- reasons:
-
- o Failover to an alternate server. Where close to real-time
- performance is required, failover thresholds need to be kept low.
- This may lead to an increased likelihood of duplicates. Failover
- can occur at the client or within Diameter agents.
-
- o Failure of a client or agent after sending a record from non-
- volatile memory, but prior to receipt of an application-layer ACK
- and deletion of the record to be sent. This will result in
- retransmission of the record soon after the client or agent has
- rebooted.
-
- o Duplicates received from RADIUS gateways. Since the
- retransmission behavior of RADIUS is not defined within [RFC2865],
- the likelihood of duplication will vary according to the
- implementation.
-
- o Implementation problems and misconfiguration.
-
- The T flag is used as an indication of an application-layer
- retransmission event, e.g., due to failover to an alternate server.
- It is defined only for request messages sent by Diameter clients or
- agents. For instance, after a reboot, a client may not know whether
-
-
-
-Fajardo, et al. Standards Track [Page 149]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- it has already tried to send the accounting records in its non-
- volatile memory before the reboot occurred. Diameter servers MAY use
- the T flag as an aid when processing requests and detecting duplicate
- messages. However, servers that do this MUST ensure that duplicates
- are found even when the first transmitted request arrives at the
- server after the retransmitted request. It can be used only in cases
- where no answer has been received from the server for a request and
- the request is sent again, (e.g., due to a failover to an alternate
- peer, due to a recovered primary peer or due to a client re-sending a
- stored record from non-volatile memory such as after reboot of a
- client or agent).
-
- In some cases, the Diameter accounting server can delay the duplicate
- detection and accounting record processing until a post-processing
- phase takes place. At that time records are likely to be sorted
- according to the included User-Name and duplicate elimination is easy
- in this case. In other situations, it may be necessary to perform
- real-time duplicate detection, such as when credit limits are imposed
- or real-time fraud detection is desired.
-
- In general, only generation of duplicates due to failover or re-
- sending of records in non-volatile storage can be reliably detected
- by Diameter clients or agents. In such cases, the Diameter client or
- agents can mark the message as a possible duplicate by setting the T
- flag. Since the Diameter server is responsible for duplicate
- detection, it can choose whether or not to make use of the T flag, in
- order to optimize duplicate detection. Since the T flag does not
- affect interoperability, and it may not be needed by some servers,
- generation of the T flag is REQUIRED for Diameter clients and agents,
- but it MAY be implemented by Diameter servers.
-
- As an example, it can be usually be assumed that duplicates appear
- within a time window of longest recorded network partition or device
- fault, perhaps a day. So only records within this time window need
- to be looked at in the backward direction. Secondly, hashing
- techniques or other schemes, such as the use of the T flag in the
- received messages, may be used to eliminate the need to do a full
- search even in this set except for rare cases.
-
- The following is an example of how the T flag may be used by the
- server to detect duplicate requests.
-
- A Diameter server MAY check the T flag of the received message to
- determine if the record is a possible duplicate. If the T flag is
- set in the request message, the server searches for a duplicate
- within a configurable duplication time window backward and
- forward. This limits database searching to those records where
- the T flag is set. In a well-run network, network partitions and
-
-
-
-Fajardo, et al. Standards Track [Page 150]
-
-RFC 6733 Diameter Base Protocol October 2012
-
-
- device faults will presumably be rare events, so this approach
- represents a substantial optimization of the duplicate detection
- process. During failover, it is possible for the original record
- to be received after the T-flag-marked record, due to differences
- in network delays experienced along the path by the original and
- duplicate transmissions. The likelihood of this occurring
- increases as the failover interval is decreased. In order to be
- able to detect duplicates that are out of order, the Diameter
- server should use backward and forward time windows when
- performing duplicate checking for the T-flag-marked request. For
- example, in order to allow time for the original record to exit
- the network and be recorded by the accounting server, the Diameter
- server can delay processing records with the T flag set until a
- time period TIME_WAIT + RECORD_PROCESSING_TIME has elapsed after
- the closing of the original transport connection. After this time
- period, it may check the T-flag-marked records against the
- database with relative assurance that the original records, if
- sent, have been received and recorded.
-
-Appendix D. Internationalized Domain Names
-
- To be compatible with the existing DNS infrastructure and simplify
- host and domain name comparison, Diameter identities (FQDNs) are
- represented in ASCII form. This allows the Diameter protocol to fall
- in-line with the DNS strategy of being transparent from the effects
- of Internationalized Domain Names (IDNs) by following the
- recommendations in [RFC4690] and [RFC5890]. Applications that
- provide support for IDNs outside of the Diameter protocol but
- interacting with it SHOULD use the representation and conversion
- framework described in [RFC5890], [RFC5891], and [RFC3492].
-</pre>
-
-</section>
diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml
index 6ca280c52b..9f84eeb9fd 100644
--- a/lib/diameter/doc/src/diameter_tcp.xml
+++ b/lib/diameter/doc/src/diameter_tcp.xml
@@ -27,7 +27,8 @@
<erlref>
<header>
<copyright>
-<year>2011</year><year>2016</year>
+<year>2011</year>
+<year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -99,7 +100,9 @@ before configuring TLS capability on diameter transports.</p>
| {rport, integer()}
| {accept, Match}
| {port, integer()}
- | {fragment_timer, infinity | 0..16#FFFFFFFF}</v>
+ | {fragment_timer, infinity | 0..16#FFFFFFFF}
+ | {message_cb, &mod_eval;}
+ | {sender, boolean()}</v>
<v>SslOpt = {ssl_options, true | list()}</v>
<v>TcpOpt = term()</v>
<v>Match = &ip_address; | string() | [Match]</v>
@@ -140,6 +143,44 @@ such a message is received over the transport interface after
two successive timeouts without the reception of additional bytes.
Defaults to 1000.</p>
+<marker id="sender"/>
+<p>
+Option <c>sender</c> specifies whether or not to use a dedicated
+process for sending outgoing messages, which avoids the possibility of
+send blocking reception.
+Defaults to <c>false</c>.
+If set to <c>true</c> then a <c>message_cb</c> that avoids the
+possibility of messages being queued in the sender process without
+bound should be configured.</p>
+
+<p>
+Option <c>message_cb</c> specifies a callback that is invoked on
+incoming and outgoing messages, that can be used to implement
+flow control.
+It is applied to two arguments: an atom indicating the
+reason for the callback (<c>send</c>, <c>recv</c>, or <c>ack</c> after
+a completed send), and the message in question (binary() on
+<c>recv</c>, binary() or diameter_packet record on <c>send</c> or
+<c>ack</c>, or <c>false</c> on <c>ack</c> when an incoming request has
+been discarded).
+It should return a list of actions and a new callback as
+tail; eg. <c>[fun cb/3, State]</c>.
+Valid actions are the atoms <c>send</c> or <c>recv</c>, to
+cause a following message-valued action to be sent/received,
+a message to send/receive (binary() or
+diameter_packet record), or a boolean() to enable/disable reading on
+the socket.
+More than one <c>send</c>/<c>recv</c>/message sequence can be
+returned from the same callback, and an initial
+<c>send</c>/<c>recv</c> can be omitted if the same as the value passed
+as the callback's first argument.
+Reading is initially enabled, and returning <c>false</c> does not
+imply there cannot be subsequent <c>recv</c> callbacks since
+messages may already have been read.
+An empty tail is equivalent to the prevailing callback.
+Defaults to a callback equivalent to <c>fun(ack, _) -> []; (_, Msg) ->
+[Msg] end</c>.</p>
+
<p>
Remaining options are any accepted by &ssl_connect3; or
&gen_tcp_connect3; for
@@ -170,14 +211,11 @@ that will not be forthcoming, which will eventually cause the RFC 3539
watchdog to take down the connection.</p>
<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>
+The first element of a non-empty <c>Host-IP-Address</c> list in
+<c>Svc</c> provides the local IP address if an <c>ip</c> option is not
+specified.
+The local 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/files.mk b/lib/diameter/doc/src/files.mk
index cb4f88a375..4c1297f6cc 100644
--- a/lib/diameter/doc/src/files.mk
+++ b/lib/diameter/doc/src/files.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2016. All Rights Reserved.
+# Copyright Ericsson AB 2010-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -40,8 +40,7 @@ XML_PART_FILES = \
user_man.xml
XML_EXTRA_FILES = \
- seealso.ent \
- diameter_soc_rfc6733.xml
+ seealso.ent
XML_CHAPTER_FILES = \
diameter_intro.xml \
diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml
index 70e1880be5..3232c3380a 100644
--- a/lib/diameter/doc/src/notes.xml
+++ b/lib/diameter/doc/src/notes.xml
@@ -11,7 +11,7 @@
<header>
<copyright>
<year>2011</year>
-<year>2016</year>
+<year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,6 +43,369 @@ first.</p>
<!-- ===================================================================== -->
+<section><title>diameter 2.1.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix documentation typos.</p>
+ <p>
+ Own Id: OTP-15045</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>diameter 2.1.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix close of diameter_tcp/sctp listening socket at
+ diameter:remove_transport/2, that was broken in diameter
+ 2.1. A reconfigured transport could not listen on the
+ same endpoint as a result.</p>
+ <p>
+ Own Id: OTP-14839</p>
+ </item>
+ <item>
+ <p>
+ Fix handling of SUSPECT connections at service
+ termination. A connection with this watchdog state caused
+ diameter_service:terminate/2 to fail.</p>
+ <p>
+ Own Id: OTP-14947 Aux Id: ERIERL-124 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>diameter 2.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix documentation typo: peer_up/3 was written where
+ peer_down/3 was intended.</p>
+ <p>
+ Own Id: OTP-14805</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>diameter 2.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A fault introduced in diameter 2.1 could cause decode
+ errors to be ignored in AVPs following the header of a
+ Grouped AVP.</p>
+ <p>
+ Own Id: OTP-14684 Aux Id: ERIERL-85 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>diameter 2.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ An inadvertently removed monitor in diameter 2.1 caused
+ the ets table diameter_reg to leak entries, and caused
+ service restart and more to fail.</p>
+ <p>
+ Own Id: OTP-14668 Aux Id: ERIERL-83 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>diameter 2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix handling of Proxy-Info in answer messages setting the
+ E-bit.</p>
+ <p>
+ RFC 6733 requires that Proxy-Info AVPs in an incoming
+ request be echoed in an outgoing answer. This was not
+ done in answers formulated by diameter; for example, as a
+ result of a handle_request callback having returned an
+ 'answer-message' or protocol_error tuple.</p>
+ <p>
+ Own Id: OTP-9869</p>
+ </item>
+ <item>
+ <p>
+ React to nodeup/nodedown when sharing peer connections.</p>
+ <p>
+ Service configuration share_peers and use_shared_peers
+ did not respond to the coming and going of remote nodes.</p>
+ <p>
+ Own Id: OTP-14011</p>
+ </item>
+ <item>
+ <p>
+ Fix inappropriate message callbacks.</p>
+ <p>
+ An incoming CER or DPR was regarded as discarded,
+ resulting in a corresponding message callback (if
+ configured) in diameter_tcp/sctp.</p>
+ <p>
+ Own Id: OTP-14486</p>
+ </item>
+ <item>
+ <p>
+ Fix handling of 5009 errors (DIAMETER_AVP_OCCURS_TOO_MANY
+ TIMES).</p>
+ <p>
+ RFC 6733 says that the first AVP that exceeds the bound
+ should be reported, but the suggestions in the errors
+ field of a diameter_packet record counted AVPs from the
+ rear of the message, not the front. Additionally,
+ diameter 2.0 in OTP 20.0 broke the counting by accepting
+ one more AVP than the message grammar in question
+ allowed.</p>
+ <p>
+ Own Id: OTP-14512</p>
+ </item>
+ <item>
+ <p>
+ Match case insensitively in diameter_tcp/sctp accept
+ tuple.</p>
+ <p>
+ Matching of remote addresses when accepting connections
+ in a listening transport was case-sensitive, causing the
+ semantics to change as a consequence of (kernel)
+ OTP-13006.</p>
+ <p>
+ Own Id: OTP-14535 Aux Id: OTP-13006 </p>
+ </item>
+ <item>
+ <p>
+ Fix backwards incompatibility of remote send when sharing
+ transports.</p>
+ <p>
+ The sending of requests over a transport connection on a
+ remote node running an older version of diameter was
+ broken by diameter 2.0 in OTP 20.0.</p>
+ <p>
+ Own Id: OTP-14552</p>
+ </item>
+ <item>
+ <p>
+ Fix diameter_packet.avps decode of Grouped AVP errors in
+ Failed-AVP.</p>
+ <p>
+ Decode didn't produce a list of diameter_avp records, so
+ information about faulty component AVPs was lost.</p>
+ <p>
+ Own Id: OTP-14607</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Let unordered delivery be configured in diameter_sctp.</p>
+ <p>
+ With option {unordered, boolean() | pos_integer()}, with
+ false the default, and N equivalent to OS =&lt; N, where
+ OS is the number of outbound streams negotiated on the
+ association in question. If configured, unordered sending
+ commences upon reception of a second message, outgoing
+ messages being sent on stream 0 before this.</p>
+ <p>
+ The default false is for backwards compatibility, but
+ false or 1 should be set to follow RFC 6733's
+ recommendation on the use of unordered sending to avoid
+ head-of-line blocking. There is typically no meaningful
+ order to preserve, since the order in which outgoing
+ messages are received by a transport process isn't known
+ to the sender.</p>
+ <p>
+ Own Id: OTP-10889</p>
+ </item>
+ <item>
+ <p>
+ Complete/simplify Standards Compliance in User's Guide.</p>
+ <p>
+ Own Id: OTP-10927</p>
+ </item>
+ <item>
+ <p>
+ Add service option decode_format.</p>
+ <p>
+ To allow incoming messages to be decoded into maps or
+ lists instead of records. Messages can be presented in
+ any of the formats for encode.</p>
+ <p>
+ Decode performance has also been improved.</p>
+ <p>
+ Own Id: OTP-14511 Aux Id: OTP-14343 </p>
+ </item>
+ <item>
+ <p>
+ Add service option traffic_counters.</p>
+ <p>
+ To let message-related counters be disabled, which can be
+ a performance improvement in some usecases.</p>
+ <p>
+ Own Id: OTP-14521</p>
+ </item>
+ <item>
+ <p>
+ Allow loopback/any as local addresses in
+ diameter_tcp/sctp.</p>
+ <p>
+ The atoms were implied by documentation, but not handled
+ in code.</p>
+ <p>
+ Own Id: OTP-14544</p>
+ </item>
+ <item>
+ <p>
+ Add transport option strict_capx.</p>
+ <p>
+ To allow the RFC 6733 requirement that a transport
+ connection be closed if a message is received before
+ capabilities exchange to be relaxed.</p>
+ <p>
+ Own Id: OTP-14546</p>
+ </item>
+ <item>
+ <p>
+ Be consistent with service/transport configuration.</p>
+ <p>
+ For options for which it's meaningful, defaults values
+ for transport options can now be configured on a service.
+ This was previously the case only for an arbitrary subset
+ of options.</p>
+ <p>
+ Own Id: OTP-14555</p>
+ </item>
+ <item>
+ <p>
+ Add service/transport option avp_dictionaries.</p>
+ <p>
+ To provide better support for AVPs that are not defined
+ in the application dictionary: configuring additional
+ dictionaries in an avp_dictionaries tuple allows their
+ AVPs to be encoded/decoded in much the same fashion as
+ application AVPs.</p>
+ <p>
+ The motivation is RFC 7683 Diameter Overload, Indicator
+ Conveyance (DOIC), that defines AVPs intended to be
+ piggybacked onto arbitrary messages. A DOIC dictionary
+ has been included in the installation, in module
+ diameter_gen_doic_rfc7683.</p>
+ <p>
+ Own Id: OTP-14588</p>
+ </item>
+ <item>
+ <p>
+ Decode application AVPs in answers setting the E-bit.</p>
+ <p>
+ AVPs defined in the application of the message being sent
+ were previously not decoded, only those in the common
+ application that defines the answer-message grammar.</p>
+ <p>
+ Own Id: OTP-14596</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>diameter 2.0</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Let candidate peers be passed to diameter:call/4</p>
+ <p>
+ With call option peer, to allow a request to be sent to a
+ peer that hasn't advertised support for the application
+ in question.</p>
+ <p>
+ RFC 6733 2.4 requires a node to send the application
+ identifiers of all locally supported applications at
+ capabilities exchange, but not all nodes respect this for
+ the common application, and diameter itself will send
+ D[WP][RA] without the common application having been
+ explicitly advertised. Regarding the common application
+ as implicit renders Result-Code 5010
+ (DIAMETER_NO_COMMON_APPLICATION) meaningless however, so
+ allow any request to be sent as long as there is a
+ configured dictionary to support it.</p>
+ <p>
+ Own Id: OTP-14338</p>
+ </item>
+ <item>
+ <p>
+ Improve performance of message encode/decode and related
+ handling.</p>
+ <p>
+ Dictionaries using @custom_types or @codecs will need to
+ adapt the corresponding functions to accept an additional
+ argument that is now passed through encode/decode, which
+ was required to remove various process dictionary-based
+ workarounds that have been used to solve problems in the
+ past.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-14343</p>
+ </item>
+ <item>
+ <p>
+ Add transport options to avoid deadlock and allow for
+ load regulation.</p>
+ <p>
+ Both diameter_tcp and diameter_sctp now accept two new
+ configuration options: sender and message_cb. The former
+ causes outgoing sends to take place in a dedicated
+ process, to avoid the possibility of deadlock when both
+ the transport process and its peer block in send. The
+ latter allows a callback to control the reading of
+ messages on the socket, to allow for backpressure towards
+ peers when the rate of incoming traffic is greater than
+ can otherwise be handled.</p>
+ <p>
+ Neither of these options are yet documented, but are
+ unlikely to change unless problems are discovered. The
+ sender option is not the default since it should probably
+ always be used in combination with message_cb, to prevent
+ incoming requests from being read at a higher rate than a
+ peer allows outgoing answers to be sent.</p>
+ <p>
+ Own Id: OTP-14455 Aux Id: ERL-332 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>diameter 1.12.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/diameter/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent
index e5c284c6e8..72d74c103c 100644
--- a/lib/diameter/doc/src/seealso.ent
+++ b/lib/diameter/doc/src/seealso.ent
@@ -4,7 +4,7 @@
%CopyrightBegin%
-Copyright Ericsson AB 2012-2015. All Rights Reserved.
+Copyright Ericsson AB 2012-2017. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -53,7 +53,7 @@ significant.
<!ENTITY mod_application_opt '<seealso marker="diameter#application_opt">diameter:application_opt()</seealso>'>
<!ENTITY mod_call_opt '<seealso marker="diameter#call_opt">diameter:call_opt()</seealso>'>
<!ENTITY mod_capability '<seealso marker="diameter#capability">diameter:capability()</seealso>'>
-<!ENTITY mod_evaluable '<seealso marker="diameter#evaluable">diameter:evaluable()</seealso>'>
+<!ENTITY mod_eval '<seealso marker="diameter#eval">diameter:eval()</seealso>'>
<!ENTITY mod_peer_filter '<seealso marker="diameter#peer_filter">diameter:peer_filter()</seealso>'>
<!ENTITY mod_service_event '<seealso marker="diameter#service_event">diameter:service_event()</seealso>'>
<!ENTITY mod_service_event_info '<seealso marker="diameter#service_event_info">diameter:service_event_info()</seealso>'>
@@ -72,13 +72,14 @@ significant.
<!ENTITY watchdog_timer '<seealso marker="#watchdog_timer">watchdog_timer</seealso>'>
<!ENTITY mod_string_decode '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso> <seealso marker="diameter#string_decode">string_decode</seealso>'>
+<!ENTITY mod_decode_format '<seealso marker="diameter#service_opt">diameter:service_opt()</seealso> <seealso marker="diameter#decode_format">decode_format</seealso>'>
<!-- diameter_app -->
<!ENTITY app_handle_answer '<seealso marker="diameter_app#Mod:handle_answer-4">handle_answer/4</seealso>'>
<!ENTITY app_handle_request '<seealso marker="diameter_app#Mod:handle_request-3">handle_request/3</seealso>'>
<!ENTITY app_handle_error '<seealso marker="diameter_app#Mod:handle_error-4">handle_error/4</seealso>'>
-<!ENTITY app_peer_down '<seealso marker="diameter_app#Mod:peer_down-3">peer_up/3</seealso>'>
+<!ENTITY app_peer_down '<seealso marker="diameter_app#Mod:peer_down-3">peer_down/3</seealso>'>
<!ENTITY app_peer_up '<seealso marker="diameter_app#Mod:peer_up-3">peer_up/3</seealso>'>
<!ENTITY app_pick_peer '<seealso marker="diameter_app#Mod:pick_peer-4">pick_peer/4</seealso>'>
<!ENTITY app_prepare_retransmit '<seealso marker="diameter_app#Mod:prepare_retransmit-3">prepare_retransmit/3</seealso>'>
diff --git a/lib/diameter/doc/standard/rfc7683.txt b/lib/diameter/doc/standard/rfc7683.txt
new file mode 100644
index 0000000000..ab2392c6c0
--- /dev/null
+++ b/lib/diameter/doc/standard/rfc7683.txt
@@ -0,0 +1,2355 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF) J. Korhonen, Ed.
+Request for Comments: 7683 Broadcom Corporation
+Category: Standards Track S. Donovan, Ed.
+ISSN: 2070-1721 B. Campbell
+ Oracle
+ L. Morand
+ Orange Labs
+ October 2015
+
+
+ Diameter Overload Indication Conveyance
+
+Abstract
+
+ This specification defines a base solution for Diameter overload
+ control, referred to as Diameter Overload Indication Conveyance
+ (DOIC).
+
+Status of This Memo
+
+ This is an Internet Standards Track document.
+
+ This document is a product of the Internet Engineering Task Force
+ (IETF). It represents the consensus of the IETF community. It has
+ received public review and has been approved for publication by the
+ Internet Engineering Steering Group (IESG). Further information on
+ Internet Standards is available in Section 2 of RFC 5741.
+
+ Information about the current status of this document, any errata,
+ and how to provide feedback on it may be obtained at
+ http://www.rfc-editor.org/info/rfc7683.
+
+Copyright Notice
+
+ Copyright (c) 2015 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 1]
+
+RFC 7683 DOIC October 2015
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2. Terminology and Abbreviations . . . . . . . . . . . . . . . . 3
+ 3. Conventions Used in This Document . . . . . . . . . . . . . . 5
+ 4. Solution Overview . . . . . . . . . . . . . . . . . . . . . . 5
+ 4.1. Piggybacking . . . . . . . . . . . . . . . . . . . . . . 6
+ 4.2. DOIC Capability Announcement . . . . . . . . . . . . . . 7
+ 4.3. DOIC Overload Condition Reporting . . . . . . . . . . . . 9
+ 4.4. DOIC Extensibility . . . . . . . . . . . . . . . . . . . 11
+ 4.5. Simplified Example Architecture . . . . . . . . . . . . . 12
+ 5. Solution Procedures . . . . . . . . . . . . . . . . . . . . . 12
+ 5.1. Capability Announcement . . . . . . . . . . . . . . . . . 12
+ 5.1.1. Reacting Node Behavior . . . . . . . . . . . . . . . 13
+ 5.1.2. Reporting Node Behavior . . . . . . . . . . . . . . . 13
+ 5.1.3. Agent Behavior . . . . . . . . . . . . . . . . . . . 14
+ 5.2. Overload Report Processing . . . . . . . . . . . . . . . 15
+ 5.2.1. Overload Control State . . . . . . . . . . . . . . . 15
+ 5.2.2. Reacting Node Behavior . . . . . . . . . . . . . . . 19
+ 5.2.3. Reporting Node Behavior . . . . . . . . . . . . . . . 20
+ 5.3. Protocol Extensibility . . . . . . . . . . . . . . . . . 22
+ 6. Loss Algorithm . . . . . . . . . . . . . . . . . . . . . . . 23
+ 6.1. Overview . . . . . . . . . . . . . . . . . . . . . . . . 23
+ 6.2. Reporting Node Behavior . . . . . . . . . . . . . . . . . 24
+ 6.3. Reacting Node Behavior . . . . . . . . . . . . . . . . . 24
+ 7. Attribute Value Pairs . . . . . . . . . . . . . . . . . . . . 25
+ 7.1. OC-Supported-Features AVP . . . . . . . . . . . . . . . . 25
+ 7.2. OC-Feature-Vector AVP . . . . . . . . . . . . . . . . . . 25
+ 7.3. OC-OLR AVP . . . . . . . . . . . . . . . . . . . . . . . 26
+ 7.4. OC-Sequence-Number AVP . . . . . . . . . . . . . . . . . 26
+ 7.5. OC-Validity-Duration AVP . . . . . . . . . . . . . . . . 26
+ 7.6. OC-Report-Type AVP . . . . . . . . . . . . . . . . . . . 27
+ 7.7. OC-Reduction-Percentage AVP . . . . . . . . . . . . . . . 27
+ 7.8. AVP Flag Rules . . . . . . . . . . . . . . . . . . . . . 28
+ 8. Error Response Codes . . . . . . . . . . . . . . . . . . . . 28
+ 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 29
+ 9.1. AVP Codes . . . . . . . . . . . . . . . . . . . . . . . . 29
+ 9.2. New Registries . . . . . . . . . . . . . . . . . . . . . 29
+ 10. Security Considerations . . . . . . . . . . . . . . . . . . . 30
+ 10.1. Potential Threat Modes . . . . . . . . . . . . . . . . . 30
+ 10.2. Denial-of-Service Attacks . . . . . . . . . . . . . . . 31
+ 10.3. Noncompliant Nodes . . . . . . . . . . . . . . . . . . . 32
+ 10.4. End-to-End Security Issues . . . . . . . . . . . . . . . 32
+ 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 34
+ 11.1. Normative References . . . . . . . . . . . . . . . . . . 34
+ 11.2. Informative References . . . . . . . . . . . . . . . . . 34
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 2]
+
+RFC 7683 DOIC October 2015
+
+
+ Appendix A. Issues Left for Future Specifications . . . . . . . 35
+ A.1. Additional Traffic Abatement Algorithms . . . . . . . . . 35
+ A.2. Agent Overload . . . . . . . . . . . . . . . . . . . . . 35
+ A.3. New Error Diagnostic AVP . . . . . . . . . . . . . . . . 35
+ Appendix B. Deployment Considerations . . . . . . . . . . . . . 35
+ Appendix C. Considerations for Applications Integrating the DOIC
+ Solution . . . . . . . . . . . . . . . . . . . . . . 36
+ C.1. Application Classification . . . . . . . . . . . . . . . 36
+ C.2. Implications of Application Type Overload . . . . . . . . 37
+ C.3. Request Transaction Classification . . . . . . . . . . . 38
+ C.4. Request Type Overload Implications . . . . . . . . . . . 39
+ Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . 41
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 42
+
+1. Introduction
+
+ This specification defines a base solution for Diameter overload
+ control, referred to as Diameter Overload Indication Conveyance
+ (DOIC), based on the requirements identified in [RFC7068].
+
+ This specification addresses Diameter overload control between
+ Diameter nodes that support the DOIC solution. The solution, which
+ is designed to apply to existing and future Diameter applications,
+ requires no changes to the Diameter base protocol [RFC6733] and is
+ deployable in environments where some Diameter nodes do not implement
+ the Diameter overload control solution defined in this specification.
+
+ A new application specification can incorporate the overload control
+ mechanism specified in this document by making it mandatory to
+ implement for the application and referencing this specification
+ normatively. It is the responsibility of the Diameter application
+ designers to define how overload control mechanisms work on that
+ application.
+
+ Note that the overload control solution defined in this specification
+ does not address all the requirements listed in [RFC7068]. A number
+ of features related to overload control are left for future
+ specifications. See Appendix A for a list of extensions that are
+ currently being considered.
+
+2. Terminology and Abbreviations
+
+ Abatement
+
+ Reaction to receipt of an overload report resulting in a reduction
+ in traffic sent to the reporting node. Abatement actions include
+ diversion and throttling.
+
+
+
+
+Korhonen, et al. Standards Track [Page 3]
+
+RFC 7683 DOIC October 2015
+
+
+ Abatement Algorithm
+
+ An extensible method requested by reporting nodes and used by
+ reacting nodes to reduce the amount of traffic sent during an
+ occurrence of overload control.
+
+ Diversion
+
+ An overload abatement treatment where the reacting node selects
+ alternate destinations or paths for requests.
+
+ Host-Routed Requests
+
+ Requests that a reacting node knows will be served by a particular
+ host, either due to the presence of a Destination-Host Attribute
+ Value Pair (AVP) or by some other local knowledge on the part of
+ the reacting node.
+
+ Overload Control State (OCS)
+
+ Internal state maintained by a reporting or reacting node
+ describing occurrences of overload control.
+
+ Overload Report (OLR)
+
+ Overload control information for a particular overload occurrence
+ sent by a reporting node.
+
+ Reacting Node
+
+ A Diameter node that acts upon an overload report.
+
+ Realm-Routed Requests
+
+ Requests sent by a reacting node where the reacting node does not
+ know to which host the request will be routed.
+
+ Reporting Node
+
+ A Diameter node that generates an overload report. (This may or
+ may not be the overloaded node.)
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 4]
+
+RFC 7683 DOIC October 2015
+
+
+ Throttling
+
+ An abatement treatment that limits the number of requests sent by
+ the reacting node. Throttling can include a Diameter Client
+ choosing to not send requests, or a Diameter Agent or Server
+ rejecting requests with appropriate error responses. In both
+ cases, the result of the throttling is a permanent rejection of
+ the transaction.
+
+3. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+ The interpretation from RFC 2119 [RFC2119] does not apply for the
+ above listed words when they are not used in all caps.
+
+4. Solution Overview
+
+ The Diameter Overload Information Conveyance (DOIC) solution allows
+ Diameter nodes to request that other Diameter nodes perform overload
+ abatement actions, that is, actions to reduce the load offered to the
+ overloaded node or realm.
+
+ A Diameter node that supports DOIC is known as a "DOIC node". Any
+ Diameter node can act as a DOIC node, including Diameter Clients,
+ Diameter Servers, and Diameter Agents. DOIC nodes are further
+ divided into "Reporting Nodes" and "Reacting Nodes." A reporting
+ node requests overload abatement by sending Overload Reports (OLRs).
+
+ A reacting node acts upon OLRs and performs whatever actions are
+ needed to fulfill the abatement requests included in the OLRs. A
+ reporting node may report overload on its own behalf or on behalf of
+ other nodes. Likewise, a reacting node may perform overload
+ abatement on its own behalf or on behalf of other nodes.
+
+ A Diameter node's role as a DOIC node is independent of its Diameter
+ role. For example, Diameter Agents may act as DOIC nodes, even
+ though they are not endpoints in the Diameter sense. Since Diameter
+ enables bidirectional applications, where Diameter Servers can send
+ requests towards Diameter Clients, a given Diameter node can
+ simultaneously act as both a reporting node and a reacting node.
+
+ Likewise, a Diameter Agent may act as a reacting node from the
+ perspective of upstream nodes, and a reporting node from the
+ perspective of downstream nodes.
+
+
+
+
+Korhonen, et al. Standards Track [Page 5]
+
+RFC 7683 DOIC October 2015
+
+
+ DOIC nodes do not generate new messages to carry DOIC-related
+ information. Rather, they "piggyback" DOIC information over existing
+ Diameter messages by inserting new AVPs into existing Diameter
+ requests and responses. Nodes indicate support for DOIC, and any
+ needed DOIC parameters, by inserting an OC-Supported-Features AVP
+ (Section 7.1) into existing requests and responses. Reporting nodes
+ send OLRs by inserting OC-OLR AVPs (Section 7.3).
+
+ A given OLR applies to the Diameter realm and application of the
+ Diameter message that carries it. If a reporting node supports more
+ than one realm and/or application, it reports independently for each
+ combination of realm and application. Similarly, the OC-Supported-
+ Features AVP applies to the realm and application of the enclosing
+ message. This implies that a node may support DOIC for one
+ application and/or realm, but not another, and may indicate different
+ DOIC parameters for each application and realm for which it supports
+ DOIC.
+
+ Reacting nodes perform overload abatement according to an agreed-upon
+ abatement algorithm. An abatement algorithm defines the meaning of
+ some of the parameters of an OLR and the procedures required for
+ overload abatement. An overload abatement algorithm separates
+ Diameter requests into two sets. The first set contains the requests
+ that are to undergo overload abatement treatment of either throttling
+ or diversion. The second set contains the requests that are to be
+ given normal routing treatment. This document specifies a single
+ "must-support" algorithm, namely, the "loss" algorithm (Section 6).
+ Future specifications may introduce new algorithms.
+
+ Overload conditions may vary in scope. For example, a single
+ Diameter node may be overloaded, in which case, reacting nodes may
+ attempt to send requests to other destinations. On the other hand,
+ an entire Diameter realm may be overloaded, in which case, such
+ attempts would do harm. DOIC OLRs have a concept of "report type"
+ (Section 7.6), where the type defines such behaviors. Report types
+ are extensible. This document defines report types for overload of a
+ specific host and for overload of an entire realm.
+
+ DOIC works through non-supporting Diameter Agents that properly pass
+ unknown AVPs unchanged.
+
+4.1. Piggybacking
+
+ There is no new Diameter application defined to carry overload-
+ related AVPs. The overload control AVPs defined in this
+ specification have been designed to be piggybacked on top of existing
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 6]
+
+RFC 7683 DOIC October 2015
+
+
+ application messages. This is made possible by adding the optional
+ overload control AVPs OC-OLR and OC-Supported-Features into existing
+ commands.
+
+ Reacting nodes indicate support for DOIC by including the
+ OC-Supported-Features AVP in all request messages originated or
+ relayed by the reacting node.
+
+ Reporting nodes indicate support for DOIC by including the
+ OC-Supported-Features AVP in all answer messages that are originated
+ or relayed by the reporting node and that are in response to a
+ request that contained the OC-Supported-Features AVP. Reporting
+ nodes may include overload reports using the OC-OLR AVP in answer
+ messages.
+
+ Note that the overload control solution does not have fixed server
+ and client roles. The DOIC node role is determined based on the
+ message type: whether the message is a request (i.e., sent by a
+ "reacting node") or an answer (i.e., sent by a "reporting node").
+ Therefore, in a typical client-server deployment, the Diameter Client
+ may report its overload condition to the Diameter Server for any
+ Diameter-Server-initiated message exchange. An example of such is
+ the Diameter Server requesting a re-authentication from a Diameter
+ Client.
+
+4.2. DOIC Capability Announcement
+
+ The DOIC solution supports the ability for Diameter nodes to
+ determine if other nodes in the path of a request support the
+ solution. This capability is referred to as DOIC Capability
+ Announcement (DCA) and is separate from the Diameter Capability
+ Exchange.
+
+ The DCA mechanism uses the OC-Supported-Features AVPs to indicate the
+ Diameter overload features supported.
+
+ The first node in the path of a Diameter request that supports the
+ DOIC solution inserts the OC-Supported-Features AVP in the request
+ message.
+
+ The individual features supported by the DOIC nodes are indicated in
+ the OC-Feature-Vector AVP. Any semantics associated with the
+ features will be defined in extension specifications that introduce
+ the features.
+
+ Note: As discussed elsewhere in the document, agents in the path
+ of the request can modify the OC-Supported-Features AVP.
+
+
+
+
+Korhonen, et al. Standards Track [Page 7]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: The DOIC solution must support deployments where Diameter
+ Clients and/or Diameter Servers do not support the DOIC solution.
+ In this scenario, Diameter Agents that support the DOIC solution
+ may handle overload abatement for the non-supporting Diameter
+ nodes. In this case, the DOIC agent will insert the OC-Supported-
+ Features AVP in requests that do not already contain one, telling
+ the reporting node that there is a DOIC node that will handle
+ overload abatement. For transactions where there was an
+ OC-Supporting-Features AVP in the request, the agent will insert
+ the OC-Supported-Features AVP in answers, telling the reacting
+ node that there is a reporting node.
+
+ The OC-Feature-Vector AVP will always contain an indication of
+ support for the loss overload abatement algorithm defined in this
+ specification (see Section 6). This ensures that a reporting node
+ always supports at least one of the advertised abatement algorithms
+ received in a request messages.
+
+ The reporting node inserts the OC-Supported-Features AVP in all
+ answer messages to requests that contained the OC-Supported-Features
+ AVP. The contents of the reporting node's OC-Supported-Features AVP
+ indicate the set of Diameter overload features supported by the
+ reporting node. This specification defines one exception -- the
+ reporting node only includes an indication of support for one
+ overload abatement algorithm, independent of the number of overload
+ abatement algorithms actually supported by the reacting node. The
+ overload abatement algorithm indicated is the algorithm that the
+ reporting node intends to use should it enter an overload condition.
+ Reacting nodes can use the indicated overload abatement algorithm to
+ prepare for possible overload reports and must use the indicated
+ overload abatement algorithm if traffic reduction is actually
+ requested.
+
+ Note that the loss algorithm defined in this document is a
+ stateless abatement algorithm. As a result, it does not require
+ any actions by reacting nodes prior to the receipt of an overload
+ report. Stateful abatement algorithms that base the abatement
+ logic on a history of request messages sent might require reacting
+ nodes to maintain state in advance of receiving an overload report
+ to ensure that the overload reports can be properly handled.
+
+ While it should only be done in exceptional circumstances and not
+ during an active occurrence of overload, a reacting node that wishes
+ to transition to a different abatement algorithm can stop advertising
+ support for the algorithm indicated by the reporting node, as long as
+ support for the loss algorithm is always advertised.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 8]
+
+RFC 7683 DOIC October 2015
+
+
+ The DCA mechanism must also allow the scenario where the set of
+ features supported by the sender of a request and by agents in the
+ path of a request differ. In this case, the agent can update the
+ OC-Supported-Features AVP to reflect the mixture of the two sets of
+ supported features.
+
+ Note: The logic to determine if the content of the OC-Supported-
+ Features AVP should be changed is out of scope for this document,
+ as is the logic to determine the content of a modified
+ OC-Supported-Features AVP. These are left to implementation
+ decisions. Care must be taken not to introduce interoperability
+ issues for downstream or upstream DOIC nodes. As such, the agent
+ must act as a fully compliant reporting node to the downstream
+ reacting node and as a fully compliant reacting node to the
+ upstream reporting node.
+
+4.3. DOIC Overload Condition Reporting
+
+ As with DOIC capability announcement, overload condition reporting
+ uses new AVPs (Section 7.3) to indicate an overload condition.
+
+ The OC-OLR AVP is referred to as an overload report. The OC-OLR AVP
+ includes the type of report, a sequence number, the length of time
+ that the report is valid, and AVPs specific to the abatement
+ algorithm.
+
+ Two types of overload reports are defined in this document: host
+ reports and realm reports.
+
+ A report of type "HOST_REPORT" is sent to indicate the overload of a
+ specific host, identified by the Origin-Host AVP of the message
+ containing the OLR, for the Application-ID indicated in the
+ transaction. When receiving an OLR of type "HOST_REPORT", a reacting
+ node applies overload abatement treatment to the host-routed requests
+ identified by the overload abatement algorithm (as defined in
+ Section 2) sent for this application to the overloaded host.
+
+ A report of type "REALM_REPORT" is sent to indicate the overload of a
+ realm for the Application-ID indicated in the transaction. The
+ overloaded realm is identified by the Destination-Realm AVP of the
+ message containing the OLR. When receiving an OLR of type
+ "REALM_REPORT", a reacting node applies overload abatement treatment
+ to realm-routed requests identified by the overload abatement
+ algorithm (as defined in Section 2) sent for this application to the
+ overloaded realm.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 9]
+
+RFC 7683 DOIC October 2015
+
+
+ This document assumes that there is a single source for realm reports
+ for a given realm, or that if multiple nodes can send realm reports,
+ that each such node has full knowledge of the overload state of the
+ entire realm. A reacting node cannot distinguish between receiving
+ realm reports from a single node or from multiple nodes.
+
+ Note: Known issues exist if there are multiple sources for
+ overload reports that apply to the same Diameter entity. Reacting
+ nodes have no way of determining the source and, as such, will
+ treat them as coming from a single source. Variance in sequence
+ numbers between the two sources can then cause incorrect overload
+ abatement treatment to be applied for indeterminate periods of
+ time.
+
+ Reporting nodes are responsible for determining the need for a
+ reduction of traffic. The method for making this determination is
+ implementation specific and depends on the type of overload report
+ being generated. A host report might be generated by tracking use of
+ resources required by the host to handle transactions for the
+ Diameter application. A realm report generally impacts the traffic
+ sent to multiple hosts and, as such, requires tracking the capacity
+ of all servers able to handle realm-routed requests for the
+ application and realm.
+
+ Once a reporting node determines the need for a reduction in traffic,
+ it uses the DOIC-defined AVPs to report on the condition. These AVPs
+ are included in answer messages sent or relayed by the reporting
+ node. The reporting node indicates the overload abatement algorithm
+ that is to be used to handle the traffic reduction in the
+ OC-Supported-Features AVP. The OC-OLR AVP is used to communicate
+ information about the requested reduction.
+
+ Reacting nodes, upon receipt of an overload report, apply the
+ overload abatement algorithm to traffic impacted by the overload
+ report. The method used to determine the requests that are to
+ receive overload abatement treatment is dependent on the abatement
+ algorithm. The loss abatement algorithm is defined in this document
+ (Section 6). Other abatement algorithms can be defined in extensions
+ to the DOIC solution.
+
+ Two types of overload abatement treatment are defined, diversion and
+ throttling. Reacting nodes are responsible for determining which
+ treatment is appropriate for individual requests.
+
+ As the conditions that lead to the generation of the overload report
+ change, the reporting node can send new overload reports requesting
+ greater reduction if the condition gets worse or less reduction if
+ the condition improves. The reporting node sends an overload report
+
+
+
+Korhonen, et al. Standards Track [Page 10]
+
+RFC 7683 DOIC October 2015
+
+
+ with a duration of zero to indicate that the overload condition has
+ ended and abatement is no longer needed.
+
+ The reacting node also determines when the overload report expires
+ based on the OC-Validity-Duration AVP in the overload report and
+ stops applying the abatement algorithm when the report expires.
+
+ Note that erroneous overload reports can be used for DoS attacks.
+ This includes the ability to indicate that a significant reduction in
+ traffic, up to and including a request for no traffic, should be sent
+ to a reporting node. As such, care should be taken to verify the
+ sender of overload reports.
+
+4.4. DOIC Extensibility
+
+ The DOIC solution is designed to be extensible. This extensibility
+ is based on existing Diameter-based extensibility mechanisms, along
+ with the DOIC capability announcement mechanism.
+
+ There are multiple categories of extensions that are expected. This
+ includes the definition of new overload abatement algorithms, the
+ definition of new report types, and the definition of new scopes of
+ messages impacted by an overload report.
+
+ A DOIC node communicates supported features by including them in the
+ OC-Feature-Vector AVP, as a sub-AVP of OC-Supported-Features. Any
+ non-backwards-compatible DOIC extensions define new values for the
+ OC-Feature-Vector AVP. DOIC extensions also have the ability to add
+ new AVPs to the OC-Supported-Features AVP, if additional information
+ about the new feature is required.
+
+ Overload reports can also be extended by adding new sub-AVPs to the
+ OC-OLR AVP, allowing reporting nodes to communicate additional
+ information about handling an overload condition.
+
+ If necessary, new extensions can also define new AVPs that are not
+ part of the OC-Supported-Features and OC-OLR group AVPs. It is,
+ however, recommended that DOIC extensions use the OC-Supported-
+ Features AVP and OC-OLR AVP to carry all DOIC-related AVPs.
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 11]
+
+RFC 7683 DOIC October 2015
+
+
+4.5. Simplified Example Architecture
+
+ Figure 1 illustrates the simplified architecture for Diameter
+ overload information conveyance.
+
+ Realm X Same or other Realms
+ <--------------------------------------> <---------------------->
+
+
+ +--------+ : (optional) :
+ |Diameter| : :
+ |Server A|--+ .--. : +--------+ : .--.
+ +--------+ | _( `. : |Diameter| : _( `. +--------+
+ +--( )--:-| Agent |-:--( )--|Diameter|
+ +--------+ | ( ` . ) ) : +--------+ : ( ` . ) ) | Client |
+ |Diameter|--+ `--(___.-' : : `--(___.-' +--------+
+ |Server B| : :
+ +--------+ : :
+
+ End-to-end Overload Indication
+ 1) <----------------------------------------------->
+ Diameter Application Y
+
+ Overload Indication A Overload Indication A'
+ 2) <----------------------> <---------------------->
+ Diameter Application Y Diameter Application Y
+
+ Figure 1: Simplified Architecture Choices for Overload Indication
+ Delivery
+
+ In Figure 1, the Diameter overload indication can be conveyed (1)
+ end-to-end between servers and clients or (2) between servers and the
+ Diameter Agent inside the realm and then between the Diameter Agent
+ and the clients.
+
+5. Solution Procedures
+
+ This section outlines the normative behavior for the DOIC solution.
+
+5.1. Capability Announcement
+
+ This section defines DOIC Capability Announcement (DCA) behavior.
+
+ Note: This specification assumes that changes in DOIC node
+ capabilities are relatively rare events that occur as a result of
+ administrative action. Reacting nodes ought to minimize changes
+ that force the reporting node to change the features being used,
+ especially during active overload conditions. But even if
+
+
+
+Korhonen, et al. Standards Track [Page 12]
+
+RFC 7683 DOIC October 2015
+
+
+ reacting nodes avoid such changes, reporting nodes still have to
+ be prepared for them to occur. For example, differing
+ capabilities between multiple reacting nodes may still force a
+ reporting node to select different features on a per-transaction
+ basis.
+
+5.1.1. Reacting Node Behavior
+
+ A reacting node MUST include the OC-Supported-Features AVP in all
+ requests. It MAY include the OC-Feature-Vector AVP, as a sub-AVP of
+ OC-Supported-Features. If it does so, it MUST indicate support for
+ the "loss" algorithm. If the reacting node is configured to support
+ features (including other algorithms) in addition to the loss
+ algorithm, it MUST indicate such support in an OC-Feature-Vector AVP.
+
+ An OC-Supported-Features AVP in answer messages indicates there is a
+ reporting node for the transaction. The reacting node MAY take
+ action, for example, creating state for some stateful abatement
+ algorithm, based on the features indicated in the OC-Feature-Vector
+ AVP.
+
+ Note: The loss abatement algorithm does not require stateful
+ behavior when there is no active overload report.
+
+ Reacting nodes need to be prepared for the reporting node to change
+ selected algorithms. This can happen at any time, including when the
+ reporting node has sent an active overload report. The reacting node
+ can minimize the potential for changes by modifying the advertised
+ abatement algorithms sent to an overloaded reporting node to the
+ currently selected algorithm and loss (or just loss if it is the
+ currently selected algorithm). This has the effect of limiting the
+ potential change in abatement algorithm from the currently selected
+ algorithm to loss, avoiding changes to more complex abatement
+ algorithms that require state to operate properly.
+
+5.1.2. Reporting Node Behavior
+
+ Upon receipt of a request message, a reporting node determines if
+ there is a reacting node for the transaction based on the presence of
+ the OC-Supported-Features AVP in the request message.
+
+ If the request message contains an OC-Supported-Features AVP, then a
+ reporting node MUST include the OC-Supported-Features AVP in the
+ answer message for that transaction.
+
+ Note: Capability announcement is done on a per-transaction basis.
+ The reporting node cannot assume that the capabilities announced
+ by a reacting node will be the same between transactions.
+
+
+
+Korhonen, et al. Standards Track [Page 13]
+
+RFC 7683 DOIC October 2015
+
+
+ A reporting node MUST NOT include the OC-Supported-Features AVP,
+ OC-OLR AVP, or any other overload control AVPs defined in extension
+ documents in response messages for transactions where the request
+ message does not include the OC-Supported-Features AVP. Lack of the
+ OC-Supported-Features AVP in the request message indicates that there
+ is no reacting node for the transaction.
+
+ A reporting node knows what overload control functionality is
+ supported by the reacting node based on the content or absence of the
+ OC-Feature-Vector AVP within the OC-Supported-Features AVP in the
+ request message.
+
+ A reporting node MUST select a single abatement algorithm in the
+ OC-Feature-Vector AVP. The abatement algorithm selected MUST
+ indicate the abatement algorithm the reporting node wants the
+ reacting node to use when the reporting node enters an overload
+ condition.
+
+ The abatement algorithm selected MUST be from the set of abatement
+ algorithms contained in the request message's OC-Feature-Vector AVP.
+
+ A reporting node that selects the loss algorithm may do so by
+ including the OC-Feature-Vector AVP with an explicit indication of
+ the loss algorithm, or it MAY omit the OC-Feature-Vector AVP. If it
+ selects a different algorithm, it MUST include the OC-Feature-Vector
+ AVP with an explicit indication of the selected algorithm.
+
+ The reporting node SHOULD indicate support for other DOIC features
+ defined in extension documents that it supports and that apply to the
+ transaction. It does so using the OC-Feature-Vector AVP.
+
+ Note: Not all DOIC features will apply to all Diameter
+ applications or deployment scenarios. The features included in
+ the OC-Feature-Vector AVP are based on local policy of the
+ reporting node.
+
+5.1.3. Agent Behavior
+
+ Diameter Agents that support DOIC can ensure that all messages
+ relayed by the agent contain the OC-Supported-Features AVP.
+
+ A Diameter Agent MAY take on reacting node behavior for Diameter
+ endpoints that do not support the DOIC solution. A Diameter Agent
+ detects that a Diameter endpoint does not support DOIC reacting node
+ behavior when there is no OC-Supported-Features AVP in a request
+ message.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 14]
+
+RFC 7683 DOIC October 2015
+
+
+ For a Diameter Agent to be a reacting node for a non-supporting
+ Diameter endpoint, the Diameter Agent MUST include the OC-Supported-
+ Features AVP in request messages it relays that do not contain the
+ OC-Supported-Features AVP.
+
+ A Diameter Agent MAY take on reporting node behavior for Diameter
+ endpoints that do not support the DOIC solution. The Diameter Agent
+ MUST have visibility to all traffic destined for the non-supporting
+ host in order to become the reporting node for the Diameter endpoint.
+ A Diameter Agent detects that a Diameter endpoint does not support
+ DOIC reporting node behavior when there is no OC-Supported-Features
+ AVP in an answer message for a transaction that contained the
+ OC-Supported-Features AVP in the request message.
+
+ If a request already has the OC-Supported-Features AVP, a Diameter
+ Agent MAY modify it to reflect the features appropriate for the
+ transaction. Otherwise, the agent relays the OC-Supported-Features
+ AVP without change.
+
+ Example: If the agent supports a superset of the features reported
+ by the reacting node, then the agent might choose, based on local
+ policy, to advertise that superset of features to the reporting
+ node.
+
+ If the Diameter Agent changes the OC-Supported-Features AVP in a
+ request message, then it is likely it will also need to modify the
+ OC-Supported-Features AVP in the answer message for the transaction.
+ A Diameter Agent MAY modify the OC-Supported-Features AVP carried in
+ answer messages.
+
+ When making changes to the OC-Supported-Features or OC-OLR AVPs, the
+ Diameter Agent needs to ensure consistency in its behavior with both
+ upstream and downstream DOIC nodes.
+
+5.2. Overload Report Processing
+
+5.2.1. Overload Control State
+
+ Both reacting and reporting nodes maintain Overload Control State
+ (OCS) for active overload conditions. The following sections define
+ behavior associated with that OCS.
+
+ The contents of the OCS in the reporting node and in the reacting
+ node represent logical constructs. The actual internal physical
+ structure of the state included in the OCS is an implementation
+ decision.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 15]
+
+RFC 7683 DOIC October 2015
+
+
+5.2.1.1. Overload Control State for Reacting Nodes
+
+ A reacting node maintains the following OCS per supported Diameter
+ application:
+
+ o a host-type OCS entry for each Destination-Host to which it sends
+ host-type requests and
+
+ o a realm-type OCS entry for each Destination-Realm to which it
+ sends realm-type requests.
+
+ A host-type OCS entry is identified by the pair of Application-ID and
+ the node's DiameterIdentity.
+
+ A realm-type OCS entry is identified by the pair of Application-ID
+ and realm.
+
+ The host-type and realm-type OCS entries include the following
+ information (the actual information stored is an implementation
+ decision):
+
+ o Sequence number (as received in OC-OLR; see Section 7.3)
+
+ o Time of expiry (derived from OC-Validity-Duration AVP received in
+ the OC-OLR AVP and time of reception of the message carrying
+ OC-OLR AVP)
+
+ o Selected abatement algorithm (as received in the OC-Supported-
+ Features AVP)
+
+ o Input data that is abatement algorithm specific (as received in
+ the OC-OLR AVP -- for example, OC-Reduction-Percentage for the
+ loss abatement algorithm)
+
+5.2.1.2. Overload Control State for Reporting Nodes
+
+ A reporting node maintains OCS entries per supported Diameter
+ application, per supported (and eventually selected) abatement
+ algorithm, and per report type.
+
+ An OCS entry is identified by the tuple of Application-ID, report
+ type, and abatement algorithm, and it includes the following
+ information (the actual information stored is an implementation
+ decision):
+
+ o Sequence number
+
+ o Validity duration
+
+
+
+Korhonen, et al. Standards Track [Page 16]
+
+RFC 7683 DOIC October 2015
+
+
+ o Expiration time
+
+ o Input data that is algorithm specific (for example, the reduction
+ percentage for the loss abatement algorithm)
+
+5.2.1.3. Reacting Node's Maintenance of Overload Control State
+
+ When a reacting node receives an OC-OLR AVP, it MUST determine if it
+ is for an existing or new overload condition.
+
+ Note: For the remainder of this section, the term "OLR" refers to
+ the combination of the contents of the received OC-OLR AVP and the
+ abatement algorithm indicated in the received OC-Supported-
+ Features AVP.
+
+ When receiving an answer message with multiple OLRs of different
+ supported report types, a reacting node MUST process each received
+ OLR.
+
+ The OLR is for an existing overload condition if a reacting node has
+ an OCS that matches the received OLR.
+
+ For a host report, this means it matches the Application-ID and the
+ host's DiameterIdentity in an existing host OCS entry.
+
+ For a realm report, this means it matches the Application-ID and the
+ realm in an existing realm OCS entry.
+
+ If the OLR is for an existing overload condition, then a reacting
+ node MUST determine if the OLR is a retransmission or an update to
+ the existing OLR.
+
+ If the sequence number for the received OLR is greater than the
+ sequence number stored in the matching OCS entry, then a reacting
+ node MUST update the matching OCS entry.
+
+ If the sequence number for the received OLR is less than or equal to
+ the sequence number in the matching OCS entry, then a reacting node
+ MUST silently ignore the received OLR. The matching OCS MUST NOT be
+ updated in this case.
+
+ If the reacting node determines that the sequence number has rolled
+ over, then the reacting node MUST update the matching OCS entry.
+ This can be determined by recognizing that the number has changed
+ from a value within 1% of the maximum value in the OC-Sequence-Number
+ AVP to a value within 1% of the minimum value in the OC-Sequence-
+ Number AVP.
+
+
+
+
+Korhonen, et al. Standards Track [Page 17]
+
+RFC 7683 DOIC October 2015
+
+
+ If the received OLR is for a new overload condition, then a reacting
+ node MUST generate a new OCS entry for the overload condition.
+
+ For a host report, this means a reacting node creates an OCS entry
+ with the Application-ID in the received message and DiameterIdentity
+ of the Origin-Host in the received message.
+
+ Note: This solution assumes that the Origin-Host AVP in the answer
+ message included by the reporting node is not changed along the
+ path to the reacting node.
+
+ For a realm report, this means a reacting node creates an OCS entry
+ with the Application-ID in the received message and realm of the
+ Origin-Realm in the received message.
+
+ If the received OLR contains a validity duration of zero ("0"), then
+ a reacting node MUST update the OCS entry as being expired.
+
+ Note: It is not necessarily appropriate to delete the OCS entry,
+ as the recommended behavior is that the reacting node slowly
+ returns to full traffic when ending an overload abatement period.
+
+ The reacting node does not delete an OCS when receiving an answer
+ message that does not contain an OC-OLR AVP (i.e., absence of OLR
+ means "no change").
+
+5.2.1.4. Reporting Node's Maintenance of Overload Control State
+
+ A reporting node SHOULD create a new OCS entry when entering an
+ overload condition.
+
+ Note: If a reporting node knows through absence of the
+ OC-Supported-Features AVP in received messages that there are no
+ reacting nodes supporting DOIC, then the reporting node can choose
+ to not create OCS entries.
+
+ When generating a new OCS entry, the sequence number SHOULD be set to
+ zero ("0").
+
+ When generating sequence numbers for new overload conditions, the new
+ sequence number MUST be greater than any sequence number in an active
+ (unexpired) overload report for the same application and report type
+ previously sent by the reporting node. This property MUST hold over
+ a reboot of the reporting node.
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 18]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: One way of addressing this over a reboot of a reporting node
+ is to use a timestamp for the first overload condition that occurs
+ after the report and to start using sequences beginning with zero
+ for subsequent overload conditions.
+
+ A reporting node MUST update an OCS entry when it needs to adjust the
+ validity duration of the overload condition at reacting nodes.
+
+ Example: If a reporting node wishes to instruct reacting nodes to
+ continue overload abatement for a longer period of time than
+ originally communicated. This also applies if the reporting node
+ wishes to shorten the period of time that overload abatement is to
+ continue.
+
+ A reporting node MUST update an OCS entry when it wishes to adjust
+ any parameters specific to the abatement algorithm, including, for
+ example, the reduction percentage used for the loss abatement
+ algorithm.
+
+ Example: If a reporting node wishes to change the reduction
+ percentage either higher (if the overload condition has worsened)
+ or lower (if the overload condition has improved), then the
+ reporting node would update the appropriate OCS entry.
+
+ A reporting node MUST increment the sequence number associated with
+ the OCS entry anytime the contents of the OCS entry are changed.
+ This will result in a new sequence number being sent to reacting
+ nodes, instructing them to process the OC-OLR AVP.
+
+ A reporting node SHOULD update an OCS entry with a validity duration
+ of zero ("0") when the overload condition ends.
+
+ Note: If a reporting node knows that the OCS entries in the
+ reacting nodes are near expiration, then the reporting node might
+ decide not to send an OLR with a validity duration of zero.
+
+ A reporting node MUST keep an OCS entry with a validity duration of
+ zero ("0") for a period of time long enough to ensure that any
+ unexpired reacting node's OCS entry created as a result of the
+ overload condition in the reporting node is deleted.
+
+5.2.2. Reacting Node Behavior
+
+ When a reacting node sends a request, it MUST determine if that
+ request matches an active OCS.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 19]
+
+RFC 7683 DOIC October 2015
+
+
+ If the request matches an active OCS, then the reacting node MUST use
+ the overload abatement algorithm indicated in the OCS to determine if
+ the request is to receive overload abatement treatment.
+
+ For the loss abatement algorithm defined in this specification, see
+ Section 6 for the overload abatement algorithm logic applied.
+
+ If the overload abatement algorithm selects the request for overload
+ abatement treatment, then the reacting node MUST apply overload
+ abatement treatment on the request. The abatement treatment applied
+ depends on the context of the request.
+
+ If diversion abatement treatment is possible (i.e., a different path
+ for the request can be selected where the overloaded node is not part
+ of the different path), then the reacting node SHOULD apply diversion
+ abatement treatment to the request. The reacting node MUST apply
+ throttling abatement treatment to requests identified for abatement
+ treatment when diversion treatment is not possible or was not
+ applied.
+
+ Note: This only addresses the case where there are two defined
+ abatement treatments, diversion and throttling. Any extension
+ that defines a new abatement treatment must also define its
+ interaction with existing treatments.
+
+ If the overload abatement treatment results in throttling of the
+ request and if the reacting node is an agent, then the agent MUST
+ send an appropriate error as defined in Section 8.
+
+ Diameter endpoints that throttle requests need to do so according to
+ the rules of the client application. Those rules will vary by
+ application and are beyond the scope of this document.
+
+ In the case that the OCS entry indicated no traffic was to be sent to
+ the overloaded entity and the validity duration expires, then
+ overload abatement associated with the overload report MUST be ended
+ in a controlled fashion.
+
+5.2.3. Reporting Node Behavior
+
+ If there is an active OCS entry, then a reporting node SHOULD include
+ the OC-OLR AVP in all answers to requests that contain the
+ OC-Supported-Features AVP and that match the active OCS entry.
+
+ Note: A request matches 1) if the Application-ID in the request
+ matches the Application-ID in any active OCS entry and 2) if the
+ report type in the OCS entry matches a report type supported by
+ the reporting node as indicated in the OC-Supported-Features AVP.
+
+
+
+Korhonen, et al. Standards Track [Page 20]
+
+RFC 7683 DOIC October 2015
+
+
+ The contents of the OC-OLR AVP depend on the selected algorithm.
+
+ A reporting node MAY choose to not resend an overload report to a
+ reacting node if it can guarantee that this overload report is
+ already active in the reacting node.
+
+ Note: In some cases (e.g., when there are one or more agents in
+ the path between reporting and reacting nodes, or when overload
+ reports are discarded by reacting nodes), a reporting node may not
+ be able to guarantee that the reacting node has received the
+ report.
+
+ A reporting node MUST NOT send overload reports of a type that has
+ not been advertised as supported by the reacting node.
+
+ Note: A reacting node implicitly advertises support for the host
+ and realm report types by including the OC-Supported-Features AVP
+ in the request. Support for other report types will be explicitly
+ indicated by new feature bits in the OC-Feature-Vector AVP.
+
+ A reporting node SHOULD explicitly indicate the end of an overload
+ occurrence by sending a new OLR with OC-Validity-Duration set to a
+ value of zero ("0"). The reporting node SHOULD ensure that all
+ reacting nodes receive the updated overload report.
+
+ A reporting node MAY rely on the OC-Validity-Duration AVP values for
+ the implicit cleanup of overload control state on the reacting node.
+
+ Note: All OLRs sent have an expiration time calculated by adding
+ the validity duration contained in the OLR to the time the message
+ was sent. Transit time for the OLR can be safely ignored. The
+ reporting node can ensure that all reacting nodes have received
+ the OLR by continuing to send it in answer messages until the
+ expiration time for all OLRs sent for that overload condition have
+ expired.
+
+ When a reporting node sends an OLR, it effectively delegates any
+ necessary throttling to downstream nodes. If the reporting node also
+ locally throttles the same set of messages, the overall number of
+ throttled requests may be higher than intended. Therefore, before
+ applying local message throttling, a reporting node needs to check if
+ these messages match existing OCS entries, indicating that these
+ messages have survived throttling applied by downstream nodes that
+ have received the related OLR.
+
+ However, even if the set of messages match existing OCS entries, the
+ reporting node can still apply other abatement methods such as
+ diversion. The reporting node might also need to throttle requests
+
+
+
+Korhonen, et al. Standards Track [Page 21]
+
+RFC 7683 DOIC October 2015
+
+
+ for reasons other than overload. For example, an agent or server
+ might have a configured rate limit for each client and might throttle
+ requests that exceed that limit, even if such requests had already
+ been candidates for throttling by downstream nodes. The reporting
+ node also has the option to send new OLRs requesting greater
+ reductions in traffic, reducing the need for local throttling.
+
+ A reporting node SHOULD decrease requested overload abatement
+ treatment in a controlled fashion to avoid oscillations in traffic.
+
+ Example: A reporting node might wait some period of time after
+ overload ends before terminating the OLR, or it might send a
+ series of OLRs indicating progressively less overload severity.
+
+5.3. Protocol Extensibility
+
+ The DOIC solution can be extended. Types of potential extensions
+ include new traffic abatement algorithms, new report types, or other
+ new functionality.
+
+ When defining a new extension that requires new normative behavior,
+ the specification must define a new feature for the OC-Feature-Vector
+ AVP. This feature bit is used to communicate support for the new
+ feature.
+
+ The extension may define new AVPs for use in the DOIC Capability
+ Announcement and for use in DOIC overload reporting. These new AVPs
+ SHOULD be defined to be extensions to the OC-Supported-Features or
+ OC-OLR AVPs defined in this document.
+
+ The Grouped AVP extension mechanisms defined in [RFC6733] apply.
+ This allows, for example, defining a new feature that is mandatory to
+ be understood even when piggybacked on an existing application.
+
+ When defining new report type values, the corresponding specification
+ must define the semantics of the new report types and how they affect
+ the OC-OLR AVP handling.
+
+ The OC-Supported-Feature and OC-OLR AVPs can be expanded with
+ optional sub-AVPs only if a legacy DOIC implementation can safely
+ ignore them without breaking backward compatibility for the given
+ OC-Report-Type AVP value. Any new sub-AVPs must not require that the
+ M-bit be set.
+
+ Documents that introduce new report types must describe any
+ limitations on their use across non-supporting agents.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 22]
+
+RFC 7683 DOIC October 2015
+
+
+ As with any Diameter specification, RFC 6733 requires all new AVPs to
+ be registered with IANA. See Section 9 for the required procedures.
+ New features (feature bits in the OC-Feature-Vector AVP) and report
+ types (in the OC-Report-Type AVP) MUST be registered with IANA.
+
+6. Loss Algorithm
+
+ This section documents the Diameter overload loss abatement
+ algorithm.
+
+6.1. Overview
+
+ The DOIC specification supports the ability for multiple overload
+ abatement algorithms to be specified. The abatement algorithm used
+ for any instance of overload is determined by the DOIC Capability
+ Announcement process documented in Section 5.1.
+
+ The loss algorithm described in this section is the default algorithm
+ that must be supported by all Diameter nodes that support DOIC.
+
+ The loss algorithm is designed to be a straightforward and stateless
+ overload abatement algorithm. It is used by reporting nodes to
+ request a percentage reduction in the amount of traffic sent. The
+ traffic impacted by the requested reduction depends on the type of
+ overload report.
+
+ Reporting nodes request the stateless reduction of the number of
+ requests by an indicated percentage. This percentage reduction is in
+ comparison to the number of messages the node otherwise would send,
+ regardless of how many requests the node might have sent in the past.
+
+ From a conceptual level, the logic at the reacting node could be
+ outlined as follows.
+
+ 1. An overload report is received, and the associated OCS is either
+ saved or updated (if required) by the reacting node.
+
+ 2. A new Diameter request is generated by the application running on
+ the reacting node.
+
+ 3. The reacting node determines that an active overload report
+ applies to the request, as indicated by the corresponding OCS
+ entry.
+
+ 4. The reacting node determines if overload abatement treatment
+ should be applied to the request. One approach that could be
+ taken for each request is to select a uniformly selected random
+ number between 1 and 100. If the random number is less than or
+
+
+
+Korhonen, et al. Standards Track [Page 23]
+
+RFC 7683 DOIC October 2015
+
+
+ equal to the indicated reduction percentage, then the request is
+ given abatement treatment; otherwise, the request is given normal
+ routing treatment.
+
+6.2. Reporting Node Behavior
+
+ The method a reporting node uses to determine the amount of traffic
+ reduction required to address an overload condition is an
+ implementation decision.
+
+ When a reporting node that has selected the loss abatement algorithm
+ determines the need to request a reduction in traffic, it includes an
+ OC-OLR AVP in answer messages as described in Section 5.2.3.
+
+ When sending the OC-OLR AVP, the reporting node MUST indicate a
+ percentage reduction in the OC-Reduction-Percentage AVP.
+
+ The reporting node MAY change the reduction percentage in subsequent
+ overload reports. When doing so, the reporting node must conform to
+ overload report handling specified in Section 5.2.3.
+
+6.3. Reacting Node Behavior
+
+ The method a reacting node uses to determine which request messages
+ are given abatement treatment is an implementation decision.
+
+ When receiving an OC-OLR in an answer message where the algorithm
+ indicated in the OC-Supported-Features AVP is the loss algorithm, the
+ reacting node MUST apply abatement treatment to the requested
+ percentage of request messages sent.
+
+ Note: The loss algorithm is a stateless algorithm. As a result,
+ the reacting node does not guarantee that there will be an
+ absolute reduction in traffic sent. Rather, it guarantees that
+ the requested percentage of new requests will be given abatement
+ treatment.
+
+ If the reacting node comes out of the 100% traffic reduction
+ (meaning, it has received an OLR indicating that no traffic should be
+ sent, as a result of the overload report timing out), the reacting
+ node sending the traffic SHOULD be conservative and, for example,
+ first send "probe" messages to learn the overload condition of the
+ overloaded node before converging to any traffic amount/rate decided
+ by the sender. Similar concerns apply in all cases when the overload
+ report times out, unless the previous overload report stated 0%
+ reduction.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 24]
+
+RFC 7683 DOIC October 2015
+
+
+ Note: The goal of this behavior is to reduce the probability of
+ overload condition thrashing where an immediate transition from
+ 100% reduction to 0% reduction results in the reporting node
+ moving quickly back into an overload condition.
+
+7. Attribute Value Pairs
+
+ This section describes the encoding and semantics of the Diameter
+ Overload Indication Attribute Value Pairs (AVPs) defined in this
+ document.
+
+ Refer to Section 4 of [RFC6733] for more information on AVPs and AVP
+ data types.
+
+7.1. OC-Supported-Features AVP
+
+ The OC-Supported-Features AVP (AVP Code 621) is of type Grouped and
+ serves two purposes. First, it announces a node's support for the
+ DOIC solution in general. Second, it contains the description of the
+ supported DOIC features of the sending node. The OC-Supported-
+ Features AVP MUST be included in every Diameter request message a
+ DOIC supporting node sends.
+
+ OC-Supported-Features ::= < AVP Header: 621 >
+ [ OC-Feature-Vector ]
+ * [ AVP ]
+
+7.2. OC-Feature-Vector AVP
+
+ The OC-Feature-Vector AVP (AVP Code 622) is of type Unsigned64 and
+ contains a 64-bit flags field of announced capabilities of a DOIC
+ node. The value of zero (0) is reserved.
+
+ The OC-Feature-Vector sub-AVP is used to announce the DOIC features
+ supported by the DOIC node, in the form of a flag-bits field in which
+ each bit announces one feature or capability supported by the node.
+ The absence of the OC-Feature-Vector AVP in request messages
+ indicates that only the default traffic abatement algorithm described
+ in this specification is supported. The absence of the OC-Feature-
+ Vector AVP in answer messages indicates that the default traffic
+ abatement algorithm described in this specification is selected
+ (while other traffic abatement algorithms may be supported), and no
+ features other than abatement algorithms are supported.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 25]
+
+RFC 7683 DOIC October 2015
+
+
+ The following capability is defined in this document:
+
+ OLR_DEFAULT_ALGO (0x0000000000000001)
+
+ When this flag is set by the a DOIC reacting node, it means that
+ the default traffic abatement (loss) algorithm is supported. When
+ this flag is set by a DOIC reporting node, it means that the loss
+ algorithm will be used for requested overload abatement.
+
+7.3. OC-OLR AVP
+
+ The OC-OLR AVP (AVP Code 623) is of type Grouped and contains the
+ information necessary to convey an overload report on an overload
+ condition at the reporting node. The application the OC-OLR AVP
+ applies to is identified by the Application-ID found in the Diameter
+ message header. The host or realm the OC-OLR AVP concerns is
+ determined from the Origin-Host AVP and/or Origin-Realm AVP found in
+ the encapsulating Diameter command. The OC-OLR AVP is intended to be
+ sent only by a reporting node.
+
+ OC-OLR ::= < AVP Header: 623 >
+ < OC-Sequence-Number >
+ < OC-Report-Type >
+ [ OC-Reduction-Percentage ]
+ [ OC-Validity-Duration ]
+ * [ AVP ]
+
+7.4. OC-Sequence-Number AVP
+
+ The OC-Sequence-Number AVP (AVP Code 624) is of type Unsigned64. Its
+ usage in the context of overload control is described in Section 5.2.
+
+ From the functionality point of view, the OC-Sequence-Number AVP is
+ used as a nonvolatile increasing counter for a sequence of overload
+ reports between two DOIC nodes for the same overload occurrence.
+ Sequence numbers are treated in a unidirectional manner, i.e., two
+ sequence numbers in each direction between two DOIC nodes are not
+ related or correlated.
+
+7.5. OC-Validity-Duration AVP
+
+ The OC-Validity-Duration AVP (AVP Code 625) is of type Unsigned32 and
+ indicates in seconds the validity time of the overload report. The
+ number of seconds is measured after reception of the first OC-OLR AVP
+ with a given value of OC-Sequence-Number AVP. The default value for
+ the OC-Validity-Duration AVP is 30 seconds. When the OC-Validity-
+ Duration AVP is not present in the OC-OLR AVP, the default value
+ applies. The maximum value for the OC-Validity-Duration AVP is
+
+
+
+Korhonen, et al. Standards Track [Page 26]
+
+RFC 7683 DOIC October 2015
+
+
+ 86,400 seconds (24 hours). If the value received in the OC-Validity-
+ Duration is greater than the maximum value, then the default value
+ applies.
+
+7.6. OC-Report-Type AVP
+
+ The OC-Report-Type AVP (AVP Code 626) is of type Enumerated. The
+ value of the AVP describes what the overload report concerns. The
+ following values are initially defined:
+
+ HOST_REPORT 0
+ The overload report is for a host. Overload abatement treatment
+ applies to host-routed requests.
+
+ REALM_REPORT 1
+ The overload report is for a realm. Overload abatement treatment
+ applies to realm-routed requests.
+
+ The values 2-4294967295 are unassigned.
+
+7.7. OC-Reduction-Percentage AVP
+
+ The OC-Reduction-Percentage AVP (AVP Code 627) is of type Unsigned32
+ and describes the percentage of the traffic that the sender is
+ requested to reduce, compared to what it otherwise would send. The
+ OC-Reduction-Percentage AVP applies to the default (loss) algorithm
+ specified in this specification. However, the AVP can be reused for
+ future abatement algorithms, if its semantics fit into the new
+ algorithm.
+
+ The value of the Reduction-Percentage AVP is between zero (0) and one
+ hundred (100). Values greater than 100 are ignored. The value of
+ 100 means that all traffic is to be throttled, i.e., the reporting
+ node is under a severe load and ceases to process any new messages.
+ The value of 0 means that the reporting node is in a stable state and
+ has no need for the reacting node to apply any traffic abatement.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 27]
+
+RFC 7683 DOIC October 2015
+
+
+7.8. AVP Flag Rules
+
+ +---------+
+ |AVP flag |
+ |rules |
+ +----+----+
+ AVP Section | |MUST|
+ Attribute Name Code Defined Value Type |MUST| NOT|
+ +--------------------------------------------------+----+----+
+ |OC-Supported-Features 621 7.1 Grouped | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Feature-Vector 622 7.2 Unsigned64 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-OLR 623 7.3 Grouped | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Sequence-Number 624 7.4 Unsigned64 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Validity-Duration 625 7.5 Unsigned32 | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Report-Type 626 7.6 Enumerated | | V |
+ +--------------------------------------------------+----+----+
+ |OC-Reduction | | |
+ | -Percentage 627 7.7 Unsigned32 | | V |
+ +--------------------------------------------------+----+----+
+
+ As described in the Diameter base protocol [RFC6733], the M-bit usage
+ for a given AVP in a given command may be defined by the application.
+
+8. Error Response Codes
+
+ When a DOIC node rejects a Diameter request due to overload, the DOIC
+ node MUST select an appropriate error response code. This
+ determination is made based on the probability of the request
+ succeeding if retried on a different path.
+
+ Note: This only applies for DOIC nodes that are not the originator
+ of the request.
+
+ A reporting node rejecting a Diameter request due to an overload
+ condition SHOULD send a DIAMETER_TOO_BUSY error response, if it can
+ assume that the same request may succeed on a different path.
+
+ If a reporting node knows or assumes that the same request will not
+ succeed on a different path, the DIAMETER_UNABLE_TO_COMPLY error
+ response SHOULD be used. Retrying would consume valuable resources
+ during an occurrence of overload.
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 28]
+
+RFC 7683 DOIC October 2015
+
+
+ For instance, if the request arrived at the reporting node without
+ a Destination-Host AVP, then the reporting node might determine
+ that there is an alternative Diameter node that could successfully
+ process the request and that retrying the transaction would not
+ negatively impact the reporting node. DIAMETER_TOO_BUSY would be
+ sent in this case.
+
+ If the request arrived at the reporting node with a Destination-
+ Host AVP populated with its own Diameter identity, then the
+ reporting node can assume that retrying the request would result
+ in it coming to the same reporting node.
+ DIAMETER_UNABLE_TO_COMPLY would be sent in this case.
+
+ A second example is when an agent that supports the DOIC solution
+ is performing the role of a reacting node for a non-supporting
+ client. Requests that are rejected as a result of DOIC throttling
+ by the agent in this scenario would generally be rejected with a
+ DIAMETER_UNABLE_TO_COMPLY response code.
+
+9. IANA Considerations
+
+9.1. AVP Codes
+
+ New AVPs defined by this specification are listed in Section 7. All
+ AVP codes are allocated from the "AVP Codes" sub-registry under the
+ "Authentication, Authorization, and Accounting (AAA) Parameters"
+ registry.
+
+9.2. New Registries
+
+ Two new registries have been created in the "AVP Specific Values"
+ sub-registry under the "Authentication, Authorization, and Accounting
+ (AAA) Parameters" registry.
+
+ A new "OC-Feature-Vector AVP Values (code 622)" registry has been
+ created. This registry contains the following:
+
+ Feature Vector Value Name
+
+ Feature Vector Value
+
+ Specification defining the new value
+
+ See Section 7.2 for the initial Feature Vector Value in the registry.
+ This specification defines the value. New values can be added to the
+ registry using the Specification Required policy [RFC5226].
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 29]
+
+RFC 7683 DOIC October 2015
+
+
+ A new "OC-Report-Type AVP Values (code 626)" registry has been
+ created. This registry contains the following:
+
+ Report Type Value Name
+
+ Report Type Value
+
+ Specification defining the new value
+
+ See Section 7.6 for the initial assignment in the registry. New
+ types can be added using the Specification Required policy [RFC5226].
+
+10. Security Considerations
+
+ DOIC gives Diameter nodes the ability to request that downstream
+ nodes send fewer Diameter requests. Nodes do this by exchanging
+ overload reports that directly effect this reduction. This exchange
+ is potentially subject to multiple methods of attack and has the
+ potential to be used as a denial-of-service (DoS) attack vector. For
+ instance, a series of injected realm OLRs with a requested reduction
+ percentage of 100% could be used to completely eliminate any traffic
+ from being sent to that realm.
+
+ Overload reports may contain information about the topology and
+ current status of a Diameter network. This information is
+ potentially sensitive. Network operators may wish to control
+ disclosure of overload reports to unauthorized parties to avoid their
+ use for competitive intelligence or to target attacks.
+
+ Diameter does not include features to provide end-to-end
+ authentication, integrity protection, or confidentiality. This may
+ cause complications when sending overload reports between non-
+ adjacent nodes.
+
+10.1. Potential Threat Modes
+
+ The Diameter protocol involves transactions in the form of requests
+ and answers exchanged between clients and servers. These clients and
+ servers may be peers, that is, they may share a direct transport
+ (e.g., TCP or SCTP) connection, or the messages may traverse one or
+ more intermediaries, known as Diameter Agents. Diameter nodes use
+ TLS, DTLS, or IPsec to authenticate peers and to provide
+ confidentiality and integrity protection of traffic between peers.
+ Nodes can make authorization decisions based on the peer identities
+ authenticated at the transport layer.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 30]
+
+RFC 7683 DOIC October 2015
+
+
+ When agents are involved, this presents an effectively transitive
+ trust model. That is, a Diameter client or server can authorize an
+ agent for certain actions, but it must trust that agent to make
+ appropriate authorization decisions about its peers, and so on.
+ Since confidentiality and integrity protection occur at the transport
+ layer, agents can read, and perhaps modify, any part of a Diameter
+ message, including an overload report.
+
+ There are several ways an attacker might attempt to exploit the
+ overload control mechanism. An unauthorized third party might inject
+ an overload report into the network. If this third party is upstream
+ of an agent, and that agent fails to apply proper authorization
+ policies, downstream nodes may mistakenly trust the report. This
+ attack is at least partially mitigated by the assumption that nodes
+ include overload reports in Diameter answers but not in requests.
+ This requires an attacker to have knowledge of the original request
+ in order to construct an answer. Such an answer would also need to
+ arrive at a Diameter node via a protected transport connection.
+ Therefore, implementations MUST validate that an answer containing an
+ overload report is a properly constructed response to a pending
+ request prior to acting on the overload report, and that the answer
+ was received via an appropriate transport connection.
+
+ A similar attack involves a compromised but otherwise authorized node
+ that sends an inappropriate overload report. For example, a server
+ for the realm "example.com" might send an overload report indicating
+ that a competitor's realm "example.net" is overloaded. If other
+ nodes act on the report, they may falsely believe that "example.net"
+ is overloaded, effectively reducing that realm's capacity.
+ Therefore, it's critical that nodes validate that an overload report
+ received from a peer actually falls within that peer's responsibility
+ before acting on the report or forwarding the report to other peers.
+ For example, an overload report from a peer that applies to a realm
+ not handled by that peer is suspect. This may require out-of-band,
+ non-Diameter agreements and/or mechanisms.
+
+ This attack is partially mitigated by the fact that the
+ application, as well as host and realm, for a given OLR is
+ determined implicitly by respective AVPs in the enclosing answer.
+ If a reporting node modifies any of those AVPs, the enclosing
+ transaction will also be affected.
+
+10.2. Denial-of-Service Attacks
+
+ Diameter overload reports, especially realm reports, can cause a node
+ to cease sending some or all Diameter requests for an extended
+ period. This makes them a tempting vector for DoS attacks.
+ Furthermore, since Diameter is almost always used in support of other
+
+
+
+Korhonen, et al. Standards Track [Page 31]
+
+RFC 7683 DOIC October 2015
+
+
+ protocols, a DoS attack on Diameter is likely to impact those
+ protocols as well. In the worst case, where the Diameter application
+ is being used for access control into an IP network, a coordinated
+ DoS attack could result in the blockage of all traffic into that
+ network. Therefore, Diameter nodes MUST NOT honor or forward OLRs
+ received from peers that are not trusted to send them.
+
+ An attacker might use the information in an OLR to assist in DoS
+ attacks. For example, an attacker could use information about
+ current overload conditions to time an attack for maximum effect, or
+ use subsequent overload reports as a feedback mechanism to learn the
+ results of a previous or ongoing attack. Operators need the ability
+ to ensure that OLRs are not leaked to untrusted parties.
+
+10.3. Noncompliant Nodes
+
+ In the absence of an overload control mechanism, Diameter nodes need
+ to implement strategies to protect themselves from floods of
+ requests, and to make sure that a disproportionate load from one
+ source does not prevent other sources from receiving service. For
+ example, a Diameter server might throttle a certain percentage of
+ requests from sources that exceed certain limits. Overload control
+ can be thought of as an optimization for such strategies, where
+ downstream nodes never send the excess requests in the first place.
+ However, the presence of an overload control mechanism does not
+ remove the need for these other protection strategies.
+
+ When a Diameter node sends an overload report, it cannot assume that
+ all nodes will comply, even if they indicate support for DOIC. A
+ noncompliant node might continue to send requests with no reduction
+ in load. Such noncompliance could be done accidentally or
+ maliciously to gain an unfair advantage over compliant nodes.
+ Requirement 28 in [RFC7068] indicates that the overload control
+ solution cannot assume that all Diameter nodes in a network are
+ trusted. It also requires that malicious nodes not be allowed to
+ take advantage of the overload control mechanism to get more than
+ their fair share of service.
+
+10.4. End-to-End Security Issues
+
+ The lack of end-to-end integrity features makes it difficult to
+ establish trust in overload reports received from non-adjacent nodes.
+ Any agents in the message path may insert or modify overload reports.
+ Nodes must trust that their adjacent peers perform proper checks on
+ overload reports from their peers, and so on, creating a transitive-
+ trust requirement extending for potentially long chains of nodes.
+ Network operators must determine if this transitive trust requirement
+ is acceptable for their deployments. Nodes supporting Diameter
+
+
+
+Korhonen, et al. Standards Track [Page 32]
+
+RFC 7683 DOIC October 2015
+
+
+ overload control MUST give operators the ability to select which
+ peers are trusted to deliver overload reports and whether they are
+ trusted to forward overload reports from non-adjacent nodes. DOIC
+ nodes MUST strip DOIC AVPs from messages received from peers that are
+ not trusted for DOIC purposes.
+
+ The lack of end-to-end confidentiality protection means that any
+ Diameter Agent in the path of an overload report can view the
+ contents of that report. In addition to the requirement to select
+ which peers are trusted to send overload reports, operators MUST be
+ able to select which peers are authorized to receive reports. A node
+ MUST NOT send an overload report to a peer not authorized to receive
+ it. Furthermore, an agent MUST remove any overload reports that
+ might have been inserted by other nodes before forwarding a Diameter
+ message to a peer that is not authorized to receive overload reports.
+
+ A DOIC node cannot always automatically detect that a peer also
+ supports DOIC. For example, a node might have a peer that is a
+ non-supporting agent. If nodes on the other side of that agent
+ send OC-Supported-Features AVPs, the agent is likely to forward
+ them as unknown AVPs. Messages received across the non-supporting
+ agent may be indistinguishable from messages received across a
+ DOIC supporting agent, giving the false impression that the non-
+ supporting agent actually supports DOIC. This complicates the
+ transitive-trust nature of DOIC. Operators need to be careful to
+ avoid situations where a non-supporting agent is mistakenly
+ trusted to enforce DOIC-related authorization policies.
+
+ It is expected that work on end-to-end Diameter security might make
+ it easier to establish trust in non-adjacent nodes for overload
+ control purposes. Readers should be reminded, however, that the
+ overload control mechanism allows Diameter Agents to modify AVPs in,
+ or insert additional AVPs into, existing messages that are originated
+ by other nodes. If end-to-end security is enabled, there is a risk
+ that such modification could violate integrity protection. The
+ details of using any future Diameter end-to-end security mechanism
+ with overload control will require careful consideration, and are
+ beyond the scope of this document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 33]
+
+RFC 7683 DOIC October 2015
+
+
+11. References
+
+11.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119,
+ DOI 10.17487/RFC2119, March 1997,
+ <http://www.rfc-editor.org/info/rfc2119>.
+
+ [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
+ IANA Considerations Section in RFCs", BCP 26, RFC 5226,
+ DOI 10.17487/RFC5226, May 2008,
+ <http://www.rfc-editor.org/info/rfc5226>.
+
+ [RFC6733] Fajardo, V., Ed., Arkko, J., Loughney, J., and G. Zorn,
+ Ed., "Diameter Base Protocol", RFC 6733,
+ DOI 10.17487/RFC6733, October 2012,
+ <http://www.rfc-editor.org/info/rfc6733>.
+
+11.2. Informative References
+
+ [Cx] 3GPP, "Cx and Dx interfaces based on the Diameter
+ protocol; Protocol details", 3GPP TS 29.229 12.7.0,
+ September 2015.
+
+ [PCC] 3GPP, "Policy and charging control architecture", 3GPP
+ TS 23.203 12.10.0, September 2015.
+
+ [RFC4006] Hakala, H., Mattila, L., Koskinen, J-P., Stura, M., and J.
+ Loughney, "Diameter Credit-Control Application", RFC 4006,
+ DOI 10.17487/RFC4006, August 2005,
+ <http://www.rfc-editor.org/info/rfc4006>.
+
+ [RFC7068] McMurry, E. and B. Campbell, "Diameter Overload Control
+ Requirements", RFC 7068, DOI 10.17487/RFC7068, November
+ 2013, <http://www.rfc-editor.org/info/rfc7068>.
+
+ [S13] 3GPP, "Evolved Packet System (EPS); Mobility Management
+ Entity (MME) and Serving GPRS Support Node (SGSN) related
+ interfaces based on Diameter protocol", 3GPP TS 29.272
+ 12.8.0, September 2015.
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 34]
+
+RFC 7683 DOIC October 2015
+
+
+Appendix A. Issues Left for Future Specifications
+
+ The base solution for overload control does not cover all possible
+ use cases. A number of solution aspects were intentionally left for
+ future specification and protocol work. The following subsections
+ define some of the potential extensions to the DOIC solution.
+
+A.1. Additional Traffic Abatement Algorithms
+
+ This specification describes only means for a simple loss-based
+ algorithm. Future algorithms can be added using the designed
+ solution extension mechanism. The new algorithms need to be
+ registered with IANA. See Sections 7.2 and 9 for the required IANA
+ steps.
+
+A.2. Agent Overload
+
+ This specification focuses on Diameter endpoint (server or client)
+ overload. A separate extension will be required to outline the
+ handling of the case of agent overload.
+
+A.3. New Error Diagnostic AVP
+
+ This specification indicates the use of existing error messages when
+ nodes reject requests due to overload. There is an expectation that
+ additional error codes or AVPs will be defined in a separate
+ specification to indicate that overload was the reason for the
+ rejection of the message.
+
+Appendix B. Deployment Considerations
+
+ Non-supporting Agents
+
+ Due to the way that realm-routed requests are handled in Diameter
+ networks with the server selection for the request done by an
+ agent, network operators should enable DOIC at agents that perform
+ server selection first.
+
+ Topology-Hiding Interactions
+
+ There exist proxies that implement what is referred to as Topology
+ Hiding. This can include cases where the agent modifies the
+ Origin-Host in answer messages. The behavior of the DOIC solution
+ is not well understood when this happens. As such, the DOIC
+ solution does not address this scenario.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 35]
+
+RFC 7683 DOIC October 2015
+
+
+ Inter-Realm/Administrative Domain Considerations
+
+ There are likely to be special considerations for handling DOIC
+ signaling across administrative boundaries. This includes
+ considerations for whether or not information included in the DOIC
+ signaling should be sent across those boundaries. In addition,
+ consideration should be taken as to whether or not a reacting node
+ in one realm can be trusted to implement the requested overload
+ abatement handling for overload reports received from a separately
+ administered realm.
+
+Appendix C. Considerations for Applications Integrating the DOIC
+ Solution
+
+ This section outlines considerations to be taken into account when
+ integrating the DOIC solution into Diameter applications.
+
+C.1. Application Classification
+
+ The following is a classification of Diameter applications and
+ request types. This discussion is meant to document factors that
+ play into decisions made by the Diameter entity responsible for
+ handling overload reports.
+
+ Section 8.1 of [RFC6733] defines two state machines that imply two
+ types of applications, session-less and session-based applications.
+ The primary difference between these types of applications is the
+ lifetime of Session-Ids.
+
+ For session-based applications, the Session-Id is used to tie
+ multiple requests into a single session.
+
+ The Credit-Control application defined in [RFC4006] is an example of
+ a Diameter session-based application.
+
+ In session-less applications, the lifetime of the Session-Id is a
+ single Diameter transaction, i.e., the session is implicitly
+ terminated after a single Diameter transaction and a new Session-Id
+ is generated for each Diameter request.
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 36]
+
+RFC 7683 DOIC October 2015
+
+
+ For the purposes of this discussion, session-less applications are
+ further divided into two types of applications:
+
+ Stateless Applications:
+
+ Requests within a stateless application have no relationship to
+ each other. The 3GPP-defined S13 application is an example of a
+ stateless application [S13], where only a Diameter command is
+ defined between a client and a server and no state is maintained
+ between two consecutive transactions.
+
+ Pseudo-Session Applications:
+
+ Applications that do not rely on the Session-Id AVP for
+ correlation of application messages related to the same session
+ but use other session-related information in the Diameter requests
+ for this purpose. The 3GPP-defined Cx application [Cx] is an
+ example of a pseudo-session application.
+
+ The handling of overload reports must take the type of application
+ into consideration, as discussed in Appendix C.2.
+
+C.2. Implications of Application Type Overload
+
+ This section discusses considerations for mitigating overload
+ reported by a Diameter entity. This discussion focuses on the type
+ of application. Appendix C.3 discusses considerations for handling
+ various request types when the target server is known to be in an
+ overloaded state.
+
+ These discussions assume that the strategy for mitigating the
+ reported overload is to reduce the overall workload sent to the
+ overloaded entity. The concept of applying overload treatment to
+ requests targeted for an overloaded Diameter entity is inherent to
+ this discussion. The method used to reduce offered load is not
+ specified here, but it could include routing requests to another
+ Diameter entity known to be able to handle them, or it could mean
+ rejecting certain requests. For a Diameter Agent, rejecting requests
+ will usually mean generating appropriate Diameter error responses.
+ For a Diameter client, rejecting requests will depend upon the
+ application. For example, it could mean giving an indication to the
+ entity requesting the Diameter service that the network is busy and
+ to try again later.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 37]
+
+RFC 7683 DOIC October 2015
+
+
+ Stateless Applications:
+
+ By definition, there is no relationship between individual
+ requests in a stateless application. As a result, when a request
+ is sent or relayed to an overloaded Diameter entity -- either a
+ Diameter Server or a Diameter Agent -- the sending or relaying
+ entity can choose to apply the overload treatment to any request
+ targeted for the overloaded entity.
+
+ Pseudo-session Applications:
+
+ For pseudo-session applications, there is an implied ordering of
+ requests. As a result, decisions about which requests towards an
+ overloaded entity to reject could take the command code of the
+ request into consideration. This generally means that
+ transactions later in the sequence of transactions should be given
+ more favorable treatment than messages earlier in the sequence.
+ This is because more work has already been done by the Diameter
+ network for those transactions that occur later in the sequence.
+ Rejecting them could result in increasing the load on the network
+ as the transactions earlier in the sequence might also need to be
+ repeated.
+
+ Session-Based Applications:
+
+ Overload handling for session-based applications must take into
+ consideration the work load associated with setting up and
+ maintaining a session. As such, the entity sending requests
+ towards an overloaded Diameter entity for a session-based
+ application might tend to reject new session requests prior to
+ rejecting intra-session requests. In addition, session-ending
+ requests might be given a lower probability of being rejected, as
+ rejecting session-ending requests could result in session status
+ being out of sync between the Diameter clients and servers.
+ Application designers that would decide to reject mid-session
+ requests will need to consider whether the rejection invalidates
+ the session and any resulting session cleanup procedures.
+
+C.3. Request Transaction Classification
+
+ Independent Request:
+
+ An independent request is not correlated to any other requests,
+ and, as such, the lifetime of the Session-Id is constrained to an
+ individual transaction.
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 38]
+
+RFC 7683 DOIC October 2015
+
+
+ Session-Initiating Request:
+
+ A session-initiating request is the initial message that
+ establishes a Diameter session. The ACR message defined in
+ [RFC6733] is an example of a session-initiating request.
+
+ Correlated Session-Initiating Request:
+
+ There are cases when multiple session-initiated requests must be
+ correlated and managed by the same Diameter server. It is notably
+ the case in the 3GPP Policy and Charging Control (PCC)
+ architecture [PCC], where multiple apparently independent Diameter
+ application sessions are actually correlated and must be handled
+ by the same Diameter server.
+
+ Intra-session Request:
+
+ An intra-session request is a request that uses the same Session-
+ Id as the one used in a previous request. An intra-session
+ request generally needs to be delivered to the server that handled
+ the session-creating request for the session. The STR message
+ defined in [RFC6733] is an example of an intra-session request.
+
+ Pseudo-session Requests:
+
+ Pseudo-session requests are independent requests and do not use
+ the same Session-Id but are correlated by other session-related
+ information contained in the request. There exist Diameter
+ applications that define an expected ordering of transactions.
+ This sequencing of independent transactions results in a pseudo-
+ session. The AIR, MAR, and SAR requests in the 3GPP-defined Cx
+ [Cx] application are examples of pseudo-session requests.
+
+C.4. Request Type Overload Implications
+
+ The request classes identified in Appendix C.3 have implications on
+ decisions about which requests should be throttled first. The
+ following list of request treatments regarding throttling is provided
+ as guidelines for application designers when implementing the
+ Diameter overload control mechanism described in this document. The
+ exact behavior regarding throttling is a matter of local policy,
+ unless specifically defined for the application.
+
+ Independent Requests:
+
+ Independent requests can generally be given equal treatment when
+ making throttling decisions, unless otherwise indicated by
+ application requirements or local policy.
+
+
+
+Korhonen, et al. Standards Track [Page 39]
+
+RFC 7683 DOIC October 2015
+
+
+ Session-Initiating Requests:
+
+ Session-initiating requests often represent more work than
+ independent or intra-session requests. Moreover, session-
+ initiating requests are typically followed by other session-
+ related requests. Since the main objective of overload control is
+ to reduce the total number of requests sent to the overloaded
+ entity, throttling decisions might favor allowing intra-session
+ requests over session-initiating requests. In the absence of
+ local policies or application-specific requirements to the
+ contrary, individual session-initiating requests can be given
+ equal treatment when making throttling decisions.
+
+ Correlated Session-Initiating Requests:
+
+ A request that results in a new binding; where the binding is used
+ for routing of subsequent session-initiating requests to the same
+ server, it represents more work load than other requests. As
+ such, these requests might be throttled more frequently than other
+ request types.
+
+ Pseudo-session Requests:
+
+ Throttling decisions for pseudo-session requests can take into
+ consideration where individual requests fit into the overall
+ sequence of requests within the pseudo-session. Requests that are
+ earlier in the sequence might be throttled more aggressively than
+ requests that occur later in the sequence.
+
+ Intra-session Requests:
+
+ There are two types of intra-sessions requests, requests that
+ terminate a session and the remainder of intra-session requests.
+ Implementers and operators may choose to throttle session-
+ terminating requests less aggressively in order to gracefully
+ terminate sessions, allow cleanup of the related resources (e.g.,
+ session state), and avoid the need for additional intra-session
+ requests. Favoring session termination requests may reduce the
+ session management impact on the overloaded entity. The default
+ handling of other intra-session requests might be to treat them
+ equally when making throttling decisions. There might also be
+ application-level considerations whether some request types are
+ favored over others.
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 40]
+
+RFC 7683 DOIC October 2015
+
+
+Contributors
+
+ The following people contributed substantial ideas, feedback, and
+ discussion to this document:
+
+ o Eric McMurry
+
+ o Hannes Tschofenig
+
+ o Ulrich Wiehe
+
+ o Jean-Jacques Trottin
+
+ o Maria Cruz Bartolome
+
+ o Martin Dolly
+
+ o Nirav Salot
+
+ o Susan Shishufeng
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 41]
+
+RFC 7683 DOIC October 2015
+
+
+Authors' Addresses
+
+ Jouni Korhonen (editor)
+ Broadcom Corporation
+ 3151 Zanker Road
+ San Jose, CA 95134
+ United States
+
+
+
+ Steve Donovan (editor)
+ Oracle
+ 7460 Warren Parkway
+ Frisco, Texas 75034
+ United States
+
+
+
+ Ben Campbell
+ Oracle
+ 7460 Warren Parkway
+ Frisco, Texas 75034
+ United States
+
+
+
+ Lionel Morand
+ Orange Labs
+ 38/40 rue du General Leclerc
+ Issy-Les-Moulineaux Cedex 9 92794
+ France
+
+ Phone: +33145296257
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Korhonen, et al. Standards Track [Page 42]
+
diff --git a/lib/diameter/examples/code/client.erl b/lib/diameter/examples/code/client.erl
index 6fb90b1c09..0864919cdd 100644
--- a/lib/diameter/examples/code/client.erl
+++ b/lib/diameter/examples/code/client.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -39,7 +39,6 @@
-module(client).
-include_lib("diameter/include/diameter.hrl").
--include_lib("diameter/include/diameter_gen_base_rfc6733.hrl").
-export([start/1, %% start a service
start/2, %%
@@ -71,6 +70,7 @@
{'Product-Name', "Client"},
{'Auth-Application-Id', [0]},
{string_decode, false},
+ {decode_format, map},
{application, [{alias, common},
{dictionary, diameter_gen_base_rfc6733},
{module, client_cb}]}]).
@@ -108,9 +108,9 @@ connect(T) ->
call(Name) ->
SId = diameter:session_id(?L(Name)),
- RAR = #diameter_base_RAR{'Session-Id' = SId,
- 'Auth-Application-Id' = 0,
- 'Re-Auth-Request-Type' = 0},
+ RAR = ['RAR' | #{'Session-Id' => SId,
+ 'Auth-Application-Id' => 0,
+ 'Re-Auth-Request-Type' => 0}],
diameter:call(Name, common, RAR, []).
call() ->
diff --git a/lib/diameter/examples/code/client_cb.erl b/lib/diameter/examples/code/client_cb.erl
index ed1d3b9b7b..af2d4d6da7 100644
--- a/lib/diameter/examples/code/client_cb.erl
+++ b/lib/diameter/examples/code/client_cb.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -55,21 +55,18 @@ prepare_request(#diameter_packet{msg = ['RAR' = T | Avps]}, _, {_, Caps}) ->
origin_realm = {OR, DR}}
= Caps,
- {send, [T, {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR}
- | Avps]};
-
-prepare_request(#diameter_packet{msg = Rec}, _, {_, Caps}) ->
- #diameter_caps{origin_host = {OH, DH},
- origin_realm = {OR, DR}}
- = Caps,
-
- {send, Rec#diameter_base_RAR{'Origin-Host' = OH,
- 'Origin-Realm' = OR,
- 'Destination-Host' = DH,
- 'Destination-Realm' = DR}}.
+ {send, [T | if is_map(Avps) ->
+ Avps#{'Origin-Host' => OH,
+ 'Origin-Realm' => OR,
+ 'Destination-Host' => DH,
+ 'Destination-Realm' => DR};
+ is_list(Avps) ->
+ [{'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', DH},
+ {'Destination-Realm', DR}
+ | Avps]
+ end]}.
%% prepare_retransmit/3
diff --git a/lib/diameter/examples/code/node.erl b/lib/diameter/examples/code/node.erl
index 246be4194b..77810bf893 100644
--- a/lib/diameter/examples/code/node.erl
+++ b/lib/diameter/examples/code/node.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@
connect/2,
stop/1]).
+-export([message/3]).
+
-type protocol()
:: tcp | sctp.
@@ -128,6 +130,8 @@ stop(Name) ->
server_opts({T, Addr, Port}) ->
[{transport_module, tmod(T)},
{transport_config, [{reuseaddr, true},
+ {sender, true},
+ {message_cb, [fun ?MODULE:message/3, 0]},
{ip, addr(Addr)},
{port, Port}]}];
@@ -173,3 +177,26 @@ addr(loopback) ->
{127,0,0,1};
addr(A) ->
A.
+
+%% ---------------------------------------------------------------------------
+
+%% message/3
+%%
+%% Simple message callback that limits the number of concurrent
+%% requests on the peer connection in question.
+
+%% Incoming request.
+message(recv, <<_:32, 1:1, _/bits>> = Bin, N) ->
+ [Bin, N < 32, fun ?MODULE:message/3, N+1];
+
+%% Outgoing request.
+message(ack, <<_:32, 1:1, _/bits>>, _) ->
+ [];
+
+%% Incoming answer or request discarded.
+message(ack, _, N) ->
+ [N =< 32, fun ?MODULE:message/3, N-1];
+
+%% Outgoing message or incoming answer.
+message(_, Bin, _) ->
+ [Bin].
diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl
index 611ad796a9..548763ec7d 100644
--- a/lib/diameter/include/diameter_gen.hrl
+++ b/lib/diameter/include/diameter_gen.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,716 +20,36 @@
%%
%% This file contains code that's included by encode/decode modules
-%% generated by diameter_codegen.erl. This code does most of the work, the
-%% generated code being kept simple.
+%% generated by diameter_codegen.erl. This code used to do most of the
+%% work, but now passes it off to module diameter_gen.
%%
--define(THROW(T), throw({?MODULE, T})).
+%% encode_avps/3
-%% Tag common to generated dictionaries.
--define(TAG, diameter_gen).
+encode_avps(Name, Avps, Opts) ->
+ diameter_gen:encode_avps(Name, Avps, Opts#{module => ?MODULE}).
-%% Key to a value in the process dictionary that determines whether or
-%% not an unrecognized AVP setting the M-bit should be regarded as an
-%% error or not. See is_strict/0. This is only used to relax M-bit
-%% interpretation inside Grouped AVPs not setting the M-bit. The
-%% service_opt() strict_mbit can be used to disable the check
-%% globally.
--define(STRICT_KEY, strict).
+%% decode_avps/2
-%% Key that says whether or not we should do a best-effort decode
-%% within Failed-AVP.
--define(FAILED_KEY, failed).
+decode_avps(Name, Avps, Opts) ->
+ diameter_gen:decode_avps(Name, Avps, Opts#{module => ?MODULE}).
--type parent_name() :: atom(). %% parent = Message or AVP
--type parent_record() :: tuple(). %%
--type avp_name() :: atom().
--type avp_record() :: tuple().
--type avp_values() :: [{avp_name(), term()}].
+%% avp/5
--type non_grouped_avp() :: #diameter_avp{}.
--type grouped_avp() :: nonempty_improper_list(#diameter_avp{}, [avp()]).
--type avp() :: non_grouped_avp() | grouped_avp().
+avp(T, Data, Name, Opts, Mod) ->
+ Mod:avp(T, Data, Name, Opts#{module := Mod}).
-%% Use a (hopefully) unique key when manipulating the process
-%% dictionary.
+%% grouped_avp/4
-putr(K,V) ->
- put({?TAG, K}, V).
+grouped_avp(T, Name, Data, Opts) ->
+ diameter_gen:grouped_avp(T, Name, Data, Opts).
-getr(K) ->
- case get({?TAG, K}) of
- undefined ->
- V = erase({?MODULE, K}), %% written in old code
- V == undefined orelse putr(K,V),
- V;
- V ->
- V
- end.
+%% empty_group/2
-eraser(K) ->
- erase({?TAG, K}).
+empty_group(Name, Opts) ->
+ diameter_gen:empty_group(Name, Opts).
-%% ---------------------------------------------------------------------------
-%% # encode_avps/2
-%% ---------------------------------------------------------------------------
+%% empty/2
--spec encode_avps(parent_name(), parent_record() | avp_values())
- -> binary()
- | no_return().
-
-encode_avps(Name, Vals)
- when is_list(Vals) ->
- encode_avps(Name, '#set-'(Vals, newrec(Name)));
-
-encode_avps(Name, Rec) ->
- try
- list_to_binary(encode(Name, Rec))
- catch
- throw: {?MODULE, Reason} ->
- diameter_lib:log({encode, error},
- ?MODULE,
- ?LINE,
- {Reason, Name, Rec}),
- erlang:error(list_to_tuple(Reason ++ [Name]));
- error: Reason ->
- Stack = erlang:get_stacktrace(),
- diameter_lib:log({encode, failure},
- ?MODULE,
- ?LINE,
- {Reason, Name, Rec, Stack}),
- erlang:error({encode_failure, Reason, Name, Stack})
- end.
-
-%% encode/2
-
-encode(Name, Rec) ->
- lists:flatmap(fun(A) -> encode(Name, A, '#get-'(A, Rec)) end,
- '#info-'(element(1, Rec), fields)).
-
-%% encode/3
-
-encode(Name, AvpName, Values) ->
- e(Name, AvpName, avp_arity(Name, AvpName), Values).
-
-%% e/4
-
-e(_, AvpName, 1, undefined) ->
- ?THROW([mandatory_avp_missing, AvpName]);
-
-e(Name, AvpName, 1, Value) ->
- e(Name, AvpName, [Value]);
-
-e(_, _, {0,_}, []) ->
- [];
-
-e(_, AvpName, _, T)
- when not is_list(T) ->
- ?THROW([repeated_avp_as_non_list, AvpName, T]);
-
-e(_, AvpName, {Min, _}, L)
- when length(L) < Min ->
- ?THROW([repeated_avp_insufficient_arity, AvpName, Min, L]);
-
-e(_, AvpName, {_, Max}, L)
- when Max < length(L) ->
- ?THROW([repeated_avp_excessive_arity, AvpName, Max, L]);
-
-e(Name, AvpName, _, Values) ->
- e(Name, AvpName, Values).
-
-%% e/3
-
-e(Name, 'AVP', Values) ->
- [pack_AVP(Name, A) || A <- Values];
-
-e(_, AvpName, Values) ->
- e(AvpName, Values).
-
-%% e/2
-
-e(AvpName, Values) ->
- H = avp_header(AvpName),
- [diameter_codec:pack_avp(H, avp(encode, V, AvpName)) || V <- Values].
-
-%% pack_AVP/2
-
-%% No value: assume AVP data is already encoded. The normal case will
-%% be when this is passed back from #diameter_packet.errors as a
-%% consequence of a failed decode. Any AVP can be encoded this way
-%% however, which side-steps any arity checks for known AVP's and
-%% could potentially encode something unfortunate.
-pack_AVP(_, #diameter_avp{value = undefined} = A) ->
- diameter_codec:pack_avp(A);
-
-%% Missing name for value encode.
-pack_AVP(_, #diameter_avp{name = N, value = V})
- when N == undefined;
- N == 'AVP' ->
- ?THROW([value_with_nameless_avp, N, V]);
-
-%% Or not. Ensure that 'AVP' is the appropriate field. Note that if we
-%% don't know this AVP at all then the encode will fail.
-pack_AVP(Name, #diameter_avp{name = AvpName,
- value = Data}) ->
- 0 == avp_arity(Name, AvpName)
- orelse ?THROW([known_avp_as_AVP, Name, AvpName, Data]),
- e(AvpName, [Data]).
-
-%% ---------------------------------------------------------------------------
-%% # decode_avps/2
-%% ---------------------------------------------------------------------------
-
--spec decode_avps(parent_name(), [#diameter_avp{}])
- -> {parent_record(), [avp()], Failed}
- when Failed :: [{5000..5999, #diameter_avp{}}].
-
-decode_avps(Name, Recs) ->
- {Avps, {Rec, Failed}}
- = lists:foldl(fun(T,A) -> decode(Name, T, A) end,
- {[], {newrec(Name), []}},
- Recs),
- {Rec, Avps, Failed ++ missing(Rec, Name, Failed)}.
-%% Append 5005 errors so that errors are reported in the order
-%% encountered. Failed-AVP should typically contain the first
-%% encountered error accordg to the RFC.
-
-newrec(Name) ->
- '#new-'(name2rec(Name)).
-
-%% 3588:
-%%
-%% DIAMETER_MISSING_AVP 5005
-%% The request did not contain an AVP that is required by the Command
-%% Code definition. If this value is sent in the Result-Code AVP, a
-%% Failed-AVP AVP SHOULD be included in the message. The Failed-AVP
-%% AVP MUST contain an example of the missing AVP complete with the
-%% Vendor-Id if applicable. The value field of the missing AVP
-%% should be of correct minimum length and contain zeros.
-
-missing(Rec, Name, Failed) ->
- Avps = lists:foldl(fun({_, #diameter_avp{code = C, vendor_id = V}}, A) ->
- sets:add_element({C,V}, A)
- end,
- sets:new(),
- Failed),
- [{5005, A} || F <- '#info-'(element(1, Rec), fields),
- not has_arity(avp_arity(Name, F), '#get-'(F, Rec)),
- #diameter_avp{code = C, vendor_id = V}
- = A <- [empty_avp(F)],
- not sets:is_element({C,V}, Avps)].
-
-%% Maximum arities have already been checked in building the record.
-
-has_arity({Min, _}, L) ->
- has_prefix(Min, L);
-has_arity(N, V) ->
- N /= 1 orelse V /= undefined.
-
-%% Compare a non-negative integer and the length of a list without
-%% computing the length.
-has_prefix(0, _) ->
- true;
-has_prefix(_, []) ->
- false;
-has_prefix(N, L) ->
- has_prefix(N-1, tl(L)).
-
-%% empty_avp/1
-
-empty_avp(Name) ->
- {Code, Flags, VId} = avp_header(Name),
- {Name, Type} = avp_name(Code, VId),
- #diameter_avp{name = Name,
- code = Code,
- vendor_id = VId,
- is_mandatory = 0 /= (Flags band 2#01000000),
- need_encryption = 0 /= (Flags band 2#00100000),
- data = empty_value(Name),
- type = Type}.
-
-%% 3588, ch 7:
-%%
-%% The Result-Code AVP describes the error that the Diameter node
-%% encountered in its processing. In case there are multiple errors,
-%% the Diameter node MUST report only the first error it encountered
-%% (detected possibly in some implementation dependent order). The
-%% specific errors that can be described by this AVP are described in
-%% the following section.
-
-%% decode/3
-
-decode(Name, #diameter_avp{code = Code, vendor_id = Vid} = Avp, Acc) ->
- decode(Name, avp_name(Code, Vid), Avp, Acc).
-
-%% decode/4
-
-%% AVP is defined in the dictionary ...
-decode(Name, {AvpName, Type}, Avp, Acc) ->
- d(Name, Avp#diameter_avp{name = AvpName, type = Type}, Acc);
-
-%% ... or not.
-decode(Name, 'AVP', Avp, Acc) ->
- decode_AVP(Name, Avp, Acc).
-
-%% 6733, 4.4:
-%%
-%% Receivers of a Grouped AVP that does not have the 'M' (mandatory)
-%% bit set and one or more of the encapsulated AVPs within the group
-%% has the 'M' (mandatory) bit set MAY simply be ignored if the
-%% Grouped AVP itself is unrecognized. The rule applies even if the
-%% encapsulated AVP with its 'M' (mandatory) bit set is further
-%% encapsulated within other sub-groups, i.e., other Grouped AVPs
-%% embedded within the Grouped AVP.
-%%
-%% The first sentence is slightly mangled, but take it to mean this:
-%%
-%% An unrecognized AVP of type Grouped that does not set the 'M' bit
-%% MAY be ignored even if one of its encapsulated AVPs sets the 'M'
-%% bit.
-%%
-%% The text above is a change from RFC 3588, which instead says this:
-%%
-%% Further, if any of the AVPs encapsulated within a Grouped AVP has
-%% the 'M' (mandatory) bit set, the Grouped AVP itself MUST also
-%% include the 'M' bit set.
-%%
-%% Both of these texts have problems. If the AVP is unknown then its
-%% type is unknown since the type isn't sent over the wire, so the
-%% 6733 text becomes a non-statement: don't know that the AVP not
-%% setting the M-bit is of type Grouped, therefore can't know that its
-%% data consists of encapsulated AVPs, therefore can't but ignore that
-%% one of these might set the M-bit. It should be no worse if we know
-%% the AVP to have type Grouped.
-%%
-%% Similarly, for the 3588 text: if we receive an AVP that doesn't set
-%% the M-bit and don't know that the AVP has type Grouped then we
-%% can't realize that its data contains an AVP that sets the M-bit, so
-%% can't regard the AVP as erroneous on this account. Again, it should
-%% be no worse if the type is known to be Grouped, but in this case
-%% the RFC forces us to regard the AVP as erroneous. This is
-%% inconsistent, and the 3588 text has never been enforced.
-%%
-%% So, if an AVP doesn't set the M-bit then we're free to ignore it,
-%% regardless of the AVP's type. If we know the type to be Grouped
-%% then we must ignore the M-bit on an encapsulated AVP. That means
-%% packing such an encapsulated AVP into an 'AVP' field if need be,
-%% not regarding the lack of a specific field as an error as is
-%% otherwise the case. (The lack of an AVP-specific field being how we
-%% defined the RFC's "unrecognized", which is slightly stronger than
-%% "not defined".)
-
-%% d/3
-
-d(Name, Avp, Acc) ->
- #diameter_avp{name = AvpName,
- data = Data,
- type = Type,
- is_mandatory = M}
- = Avp,
-
- %% Use the process dictionary is to keep track of whether or not
- %% to ignore an M-bit on an encapsulated AVP. Not ideal, but the
- %% alternative requires widespread changes to be able to pass the
- %% value around through the entire decode. The solution here is
- %% simple in comparison, both to implement and to understand.
-
- Strict = relax(Type, M),
-
- %% Use the process dictionary again to keep track of whether we're
- %% decoding within Failed-AVP and should ignore decode errors
- %% altogether.
-
- Failed = relax(Name), %% Not AvpName or else a failed Failed-AVP
- %% decode is packed into 'AVP'.
- Mod = dict(Failed), %% Dictionary to decode in.
-
- %% On decode, a Grouped AVP is represented as a #diameter_avp{}
- %% list with AVP as head and component AVPs as tail. On encode,
- %% data can be a list of component AVPs.
-
- try Mod:avp(decode, Data, AvpName) of
- V ->
- {Avps, T} = Acc,
- {H, A} = ungroup(V, Avp),
- {[H | Avps], pack_avp(Name, A, T)}
- catch
- throw: {?TAG, {grouped, Error, ComponentAvps}} ->
- g(is_failed(), Error, Name, trim(Avp), Acc, ComponentAvps);
- error: Reason ->
- d(is_failed(), Reason, Name, trim(Avp), Acc)
- after
- reset(?STRICT_KEY, Strict),
- reset(?FAILED_KEY, Failed)
- end.
-
-%% trim/1
-%%
-%% Remove any extra bit that was added in diameter_codec to induce a
-%% 5014 error.
-
-trim(#diameter_avp{data = <<0:1, Bin/binary>>} = Avp) ->
- Avp#diameter_avp{data = Bin};
-
-trim(Avps)
- when is_list(Avps) ->
- lists:map(fun trim/1, Avps);
-
-trim(Avp) ->
- Avp.
-
-%% dict/1
-%%
-%% Retrieve the dictionary for the best-effort decode of Failed-AVP,
-%% as put by diameter_codec:decode/2. See that function for the
-%% explanation.
-
-dict(true) ->
- case get({diameter_codec, dictionary}) of
- undefined ->
- ?MODULE;
- Mod ->
- Mod
- end;
-
-dict(_) ->
- ?MODULE.
-
-%% g/5
-
-%% Ignore decode errors within Failed-AVP (best-effort) ...
-g(true, [_Error | Rec], Name, Avp, Acc, _ComponentAvps) ->
- decode_AVP(Name, Avp#diameter_avp{value = Rec}, Acc);
-g(true, _Error, Name, Avp, Acc, _ComponentAvps) ->
- decode_AVP(Name, Avp, Acc);
-
-%% ... or not.
-g(false, [Error | _Rec], _Name, Avp, Acc, ComponentAvps) ->
- g(Error, Avp, Acc, ComponentAvps);
-g(false, Error, _Name, Avp, Acc, ComponentAvps) ->
- g(Error, Avp, Acc, ComponentAvps).
-
-%% g/4
-
-g({RC, ErrorData}, Avp, Acc, ComponentAvps) ->
- {Avps, {Rec, Errors}} = Acc,
- E = Avp#diameter_avp{data = [ErrorData]},
- {[[Avp | trim(ComponentAvps)] | Avps], {Rec, [{RC, E} | Errors]}}.
-
-%% d/5
-
-%% Ignore a decode error within Failed-AVP ...
-d(true, _, Name, Avp, Acc) ->
- decode_AVP(Name, Avp, Acc);
-
-%% ... or not. Failures here won't be visible since they're a "normal"
-%% occurrence if the peer sends a faulty AVP that we need to respond
-%% sensibly to. Log the occurence for traceability, but the peer will
-%% also receive info in the resulting answer message.
-d(false, Reason, Name, Avp, {Avps, Acc}) ->
- Stack = diameter_lib:get_stacktrace(),
- diameter_lib:log(decode_error,
- ?MODULE,
- ?LINE,
- {Name, Avp#diameter_avp.name, Stack}),
- {Rec, Failed} = Acc,
- {[Avp|Avps], {Rec, [rc(Reason, Avp) | Failed]}}.
-
-%% relax/2
-
-%% Set false in the process dictionary as soon as we see a Grouped AVP
-%% that doesn't set the M-bit, so that is_strict() can say whether or
-%% not to ignore the M-bit on an encapsulated AVP.
-relax('Grouped', M) ->
- case getr(?STRICT_KEY) of
- undefined when not M ->
- putr(?STRICT_KEY, M);
- _ ->
- false
- end;
-relax(_, _) ->
- false.
-
-is_strict() ->
- diameter_codec:getopt(strict_mbit)
- andalso false /= getr(?STRICT_KEY).
-
-%% relax/1
-%%
-%% Set true in the process dictionary as soon as we see Failed-AVP.
-%% Matching on 'Failed-AVP' assumes that this is the RFC AVP.
-%% Strictly, this doesn't need to be the case.
-
-relax('Failed-AVP') ->
- putr(?FAILED_KEY, true);
-
-relax(_) ->
- is_failed().
-
-%% is_failed/0
-%%
-%% Is the AVP currently being decoded nested within Failed-AVP? Note
-%% that this is only true when Failed-AVP is the parent. In
-%% particular, it's not true when Failed-AVP itself is being decoded
-%% (unless nested).
-
-is_failed() ->
- true == getr(?FAILED_KEY).
-
-%% is_failed/1
-
-is_failed(Name) ->
- 'Failed-AVP' == Name orelse is_failed().
-
-%% reset/2
-
-reset(Key, undefined) ->
- eraser(Key);
-reset(_, _) ->
- ok.
-
-%% decode_AVP/3
-%%
-%% Don't know this AVP: see if it can be packed in an 'AVP' field
-%% undecoded. Note that the type field is 'undefined' in this case.
-
-decode_AVP(Name, Avp, {Avps, Acc}) ->
- {[trim(Avp) | Avps], pack_AVP(Name, Avp, Acc)}.
-
-%% rc/1
-
-%% diameter_types will raise an error of this form to communicate
-%% DIAMETER_INVALID_AVP_LENGTH (5014). A module specified to a
-%% @custom_types tag in a dictionary file can also raise an error of
-%% this form.
-rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = Avp) ->
- {RC, Avp#diameter_avp{data = empty_value(AvpName)}};
-
-%% 3588:
-%%
-%% DIAMETER_INVALID_AVP_VALUE 5004
-%% The request contained an AVP with an invalid value in its data
-%% portion. A Diameter message indicating this error MUST include
-%% the offending AVPs within a Failed-AVP AVP.
-rc(_, Avp) ->
- {5004, Avp}.
-
-%% ungroup/2
-
--spec ungroup(term(), #diameter_avp{})
- -> {avp(), #diameter_avp{}}.
-
-%% The decoded value in the Grouped case is as returned by grouped_avp/3:
-%% a record and a list of component AVP's.
-ungroup(V, #diameter_avp{type = 'Grouped'} = Avp) ->
- {Rec, As} = V,
- A = Avp#diameter_avp{value = Rec},
- {[A|As], A};
-
-%% Otherwise it's just a plain value.
-ungroup(V, #diameter_avp{} = Avp) ->
- A = Avp#diameter_avp{value = V},
- {A, A}.
-
-%% pack_avp/3
-
-pack_avp(Name, #diameter_avp{name = AvpName} = Avp, Acc) ->
- pack_avp(Name, avp_arity(Name, AvpName), Avp, Acc).
-
-%% pack_avp/4
-
-pack_avp(Name, 0, Avp, Acc) ->
- pack_AVP(Name, Avp, Acc);
-
-pack_avp(_, Arity, Avp, Acc) ->
- pack(Arity, Avp#diameter_avp.name, Avp, Acc).
-
-%% pack_AVP/3
-
-%% Length failure was induced because of a header/payload length
-%% mismatch. The AVP Length is reset to match the received data if
-%% this AVP is encoded in an answer message, since the length is
-%% computed.
-%%
-%% Data is a truncated header if command_code = undefined, otherwise
-%% payload bytes. The former is padded to the length of a header if
-%% the AVP reaches an outgoing encode in diameter_codec.
-%%
-%% RFC 6733 says that an AVP returned with 5014 can contain a minimal
-%% payload for the AVP's type, but in this case we don't know the
-%% type.
-
-pack_AVP(_, #diameter_avp{data = <<0:1, Data/binary>>} = Avp, Acc) ->
- {Rec, Failed} = Acc,
- {Rec, [{5014, Avp#diameter_avp{data = Data}} | Failed]};
-
-pack_AVP(Name, #diameter_avp{is_mandatory = M, name = AvpName} = Avp, Acc) ->
- case pack_arity(Name, AvpName, M) of
- 0 ->
- {Rec, Failed} = Acc,
- {Rec, [{if M -> 5001; true -> 5008 end, Avp} | Failed]};
- Arity ->
- pack(Arity, 'AVP', Avp, Acc)
- end.
-
-%% Give Failed-AVP special treatment since (1) it'll contain any
-%% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to
-%% allow for Failed-AVP in an answer-message.
-
-pack_arity(Name, AvpName, M) ->
-
- %% Not testing just Name /= 'Failed-AVP' means we're changing the
- %% packing of AVPs nested within Failed-AVP, but the point of
- %% ignoring errors within Failed-AVP is to decode as much as
- %% possible, and failing because a mandatory AVP couldn't be
- %% packed into a dedicated field defeats that point. Note
- %% is_failed/1 since is_failed/0 will return false when packing
- %% 'AVP' within Failed-AVP.
-
- pack_arity(is_failed(Name)
- orelse {Name, AvpName} == {'answer-message', 'Failed-AVP'}
- orelse not M
- orelse not is_strict(),
- Name).
-
-pack_arity(true, Name) ->
- avp_arity(Name, 'AVP');
-
-pack_arity(false, _) ->
- 0.
-
-%% 3588:
-%%
-%% DIAMETER_AVP_UNSUPPORTED 5001
-%% The peer received a message that contained an AVP that is not
-%% recognized or supported and was marked with the Mandatory bit. A
-%% Diameter message with this error MUST contain one or more Failed-
-%% AVP AVP containing the AVPs that caused the failure.
-%%
-%% DIAMETER_AVP_NOT_ALLOWED 5008
-%% A message was received with an AVP that MUST NOT be present. The
-%% Failed-AVP AVP MUST be included and contain a copy of the
-%% offending AVP.
-
-%% pack/4
-
-pack(Arity, FieldName, Avp, {Rec, _} = Acc) ->
- pack('#get-'(FieldName, Rec), Arity, FieldName, Avp, Acc).
-
-%% pack/5
-
-pack(undefined, 1, FieldName, Avp, Acc) ->
- p(FieldName, fun(V) -> V end, Avp, Acc);
-
-%% 3588:
-%%
-%% DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009
-%% A message was received that included an AVP that appeared more
-%% often than permitted in the message definition. The Failed-AVP
-%% AVP MUST be included and contain a copy of the first instance of
-%% the offending AVP that exceeded the maximum number of occurrences
-%%
-
-pack(_, 1, _, Avp, {Rec, Failed}) ->
- {Rec, [{5009, Avp} | Failed]};
-pack(L, {_, Max}, FieldName, Avp, Acc) ->
- case '*' /= Max andalso has_prefix(Max, L) of
- true ->
- {Rec, Failed} = Acc,
- {Rec, [{5009, Avp} | Failed]};
- false ->
- p(FieldName, fun(V) -> [V|L] end, Avp, Acc)
- end.
-
-%% p/4
-
-p(F, Fun, Avp, {Rec, Failed}) ->
- {'#set-'({F, Fun(value(F, Avp))}, Rec), Failed}.
-
-value('AVP', Avp) ->
- Avp;
-value(_, Avp) ->
- Avp#diameter_avp.value.
-
-%% ---------------------------------------------------------------------------
-%% # grouped_avp/3
-%% ---------------------------------------------------------------------------
-
--spec grouped_avp(decode, avp_name(), bitstring())
- -> {avp_record(), [avp()]};
- (encode, avp_name(), avp_record() | avp_values())
- -> binary()
- | no_return().
-
-%% Length error induced by diameter_codec:collect_avps/1: the AVP
-%% length in the header was too short (insufficient for the extracted
-%% header) or too long (past the end of the message). An empty payload
-%% is sufficient according to the RFC text for 5014.
-grouped_avp(decode, _Name, <<0:1, _/binary>>) ->
- throw({?TAG, {grouped, {5014, []}, []}});
-
-grouped_avp(decode, Name, Data) ->
- grouped_decode(Name, diameter_codec:collect_avps(Data));
-
-grouped_avp(encode, Name, Data) ->
- encode_avps(Name, Data).
-
-%% grouped_decode/2
-%%
-%% Note that Grouped is the only AVP type that doesn't just return a
-%% decoded value, also returning the list of component diameter_avp
-%% records.
-
-%% Length error in trailing component AVP.
-grouped_decode(_Name, {Error, Acc}) ->
- {5014, Avp} = Error,
- throw({?TAG, {grouped, Error, [Avp | Acc]}});
-
-%% 7.5. Failed-AVP AVP
-
-%% In the case where the offending AVP is embedded within a Grouped AVP,
-%% the Failed-AVP MAY contain the grouped AVP, which in turn contains
-%% the single offending AVP. The same method MAY be employed if the
-%% grouped AVP itself is embedded in yet another grouped AVP and so on.
-%% In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up
-%% to the single offending AVP. This enables the recipient to detect
-%% the location of the offending AVP when embedded in a group.
-
-%% An error in decoding a component AVP throws the first fauly
-%% component, which the catch in d/3 wraps in the Grouped AVP in
-%% question. A partially decoded record is only used when ignoring
-%% errors in Failed-AVP.
-grouped_decode(Name, ComponentAvps) ->
- {Rec, Avps, Es} = decode_avps(Name, ComponentAvps),
- [] == Es orelse throw({?TAG, {grouped, [{_,_} = hd(Es) | Rec], Avps}}),
- {Rec, Avps}.
-
-%% ---------------------------------------------------------------------------
-%% # empty_group/1
-%% ---------------------------------------------------------------------------
-
-empty_group(Name) ->
- list_to_binary(empty_body(Name)).
-
-empty_body(Name) ->
- [z(F, avp_arity(Name, F)) || F <- '#info-'(name2rec(Name), fields)].
-
-z(Name, 1) ->
- z(Name);
-z(_, {0,_}) ->
- [];
-z(Name, {Min, _}) ->
- lists:duplicate(Min, z(Name)).
-
-z('AVP') ->
- <<0:64/integer>>; %% minimal header
-z(Name) ->
- Bin = diameter_codec:pack_avp(avp_header(Name), empty_value(Name)),
- << <<0>> || <<_>> <= Bin >>.
-
-%% ---------------------------------------------------------------------------
-%% # empty/1
-%% ---------------------------------------------------------------------------
-
-empty(AvpName) ->
- avp(encode, zero, AvpName).
+empty(Name, Opts) ->
+ diameter_gen:empty(Name, Opts).
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 6bf748a727..98636ed6e2 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2016. All Rights Reserved.
+# Copyright Ericsson AB 2010-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -149,7 +149,7 @@ gen/$(DICT_YRL).erl: compiler/$(DICT_YRL).yrl
$(ERLC) -Werror -o $(@D) $<
# Generate the app file.
-$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk app.sed
+$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk
$(gen_verbose) \
M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \
C=`echo $(COMPILER_MODULES) | tr ' ' ,`; \
@@ -160,8 +160,7 @@ $(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk app.sed
-e "s;%COMPILER%;$$C;" \
-e "s;%INFO%;$$I;" \
-e "s;%REGISTERED%;$$R;" \
- $< \
- | sed -f app.sed > $@
+ $< > $@
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
$(vsn_verbose) \
@@ -274,9 +273,7 @@ gen/diameter_gen_base_accounting.erl gen/diameter_gen_base_accounting.hrl: \
gen/diameter_gen_acct_rfc6733.erl gen/diameter_gen_acct_rfc6733.hrl: \
$(EBIN)/diameter_gen_base_rfc6733.$(EMULATOR)
-gen/diameter_gen_relay.erl gen/diameter_gen_relay.hrl \
-gen/diameter_gen_base_rfc3588.erl gen/diameter_gen_base_rfc3588.hrl \
-gen/diameter_gen_base_rfc6733.erl gen/diameter_gen_base_rfc6733.hrl: \
+$(DICT_ERLS) $(DICT_HRLS): \
$(COMPILER_MODULES:%=$(EBIN)/%.$(EMULATOR))
$(DICT_MODULES:gen/%=$(EBIN)/%.$(EMULATOR)): \
diff --git a/lib/diameter/src/app.sed b/lib/diameter/src/app.sed
deleted file mode 100644
index dd3806f5f1..0000000000
--- a/lib/diameter/src/app.sed
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2014-2016. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# %CopyrightEnd%
-
-#
-# Generate runtime_dependencies from applications to avoid having to
-# specify the same application more than once.
-#
-
-/{runtime_dependencies,/b v
-/{[-a-z]*, "[0-9.]*"}/!b
-/{vsn,/b
-
-/%%/!H
-s/{\([^,]*\)[^}]*}/\1/g
-s/%%/%,/
-b
-
-:v
-
-p
-x
-s/\n//
-s/%//g
-s/\n */ /g
-s/{\([^,]*\), "\([^"]*"\)}/"\1-\2/g
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index 253f64133c..b90b794611 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -46,7 +46,10 @@
-export([start/0,
stop/0]).
--export_type([evaluable/0,
+-export_type([eval/0,
+ evaluable/0, %% deprecated
+ decode_format/0,
+ strict_arities/0,
restriction/0,
message_length/0,
remotes/0,
@@ -299,7 +302,7 @@ call(SvcName, App, Message) ->
| realm
| {host, any|'DiameterIdentity'()}
| {realm, any|'DiameterIdentity'()}
- | {eval, evaluable()}
+ | {eval, eval()}
| {neg, peer_filter()}
| {all, [peer_filter()]}
| {any, [peer_filter()]}.
@@ -307,10 +310,13 @@ call(SvcName, App, Message) ->
-opaque peer_ref()
:: pid().
--type evaluable()
+-type eval()
:: {module(), atom(), list()}
| fun()
- | maybe_improper_list(evaluable(), list()).
+ | maybe_improper_list(eval(), list()).
+
+-type evaluable()
+ :: eval().
-type sequence()
:: {'Unsigned32'(), 0..32}.
@@ -320,29 +326,61 @@ call(SvcName, App, Message) ->
| node
| nodes
| [node()]
- | evaluable().
+ | eval().
-type remotes()
:: boolean()
| [node()]
- | evaluable().
+ | eval().
-type message_length()
:: 0..16#FFFFFF.
+-type decode_format()
+ :: record
+ | list
+ | map
+ | none
+ | record_from_map.
+
+-type strict_arities()
+ :: false
+ | encode
+ | decode.
+
+%% Options common to both start_service/2 and add_transport/2.
+
+-type common_opt()
+ :: {pool_size, pos_integer()}
+ | {capabilities_cb, eval()}
+ | {capx_timeout, 'Unsigned32'()}
+ | {strict_capx, boolean()}
+ | {strict_mbit, boolean()}
+ | {avp_dictionaries, [module()]}
+ | {disconnect_cb, eval()}
+ | {dpr_timeout, 'Unsigned32'()}
+ | {dpa_timeout, 'Unsigned32'()}
+ | {incoming_maxlen, message_length()}
+ | {length_errors, exit | handle | discard}
+ | {connect_timer, 'Unsigned32'()}
+ | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
+ | {watchdog_config, [{okay|suspect, non_neg_integer()}]}
+ | {spawn_opt, list()}.
+
%% Options passed to start_service/2
-type service_opt()
:: capability()
| {application, [application_opt()]}
| {restrict_connections, restriction()}
- | {sequence, sequence() | evaluable()}
+ | {sequence, sequence() | eval()}
| {share_peers, remotes()}
+ | {decode_format, decode_format()}
+ | {traffic_counters, boolean()}
| {string_decode, boolean()}
- | {strict_mbit, boolean()}
- | {incoming_maxlen, message_length()}
+ | {strict_arities, true | strict_arities()}
| {use_shared_peers, remotes()}
- | {spawn_opt, list()}.
+ | common_opt().
-type application_opt()
:: {alias, app_alias()}
@@ -372,20 +410,9 @@ call(SvcName, App, Message) ->
:: {transport_module, atom()}
| {transport_config, any()}
| {transport_config, any(), 'Unsigned32'() | infinity}
- | {pool_size, pos_integer()}
| {applications, [app_alias()]}
| {capabilities, [capability()]}
- | {capabilities_cb, evaluable()}
- | {capx_timeout, 'Unsigned32'()}
- | {capx_strictness, boolean()}
- | {disconnect_cb, evaluable()}
- | {dpr_timeout, 'Unsigned32'()}
- | {dpa_timeout, 'Unsigned32'()}
- | {length_errors, exit | handle | discard}
- | {connect_timer, 'Unsigned32'()}
- | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
- | {watchdog_config, [{okay|suspect, non_neg_integer()}]}
- | {spawn_opt, list()}
+ | common_opt()
| {private, any()}.
%% Predicate passed to remove_transport/2
@@ -406,4 +433,5 @@ call(SvcName, App, Message) ->
:: {extra, list()}
| {filter, peer_filter()}
| {timeout, 'Unsigned32'()}
+ | {peer, peer_ref()}
| detach.
diff --git a/lib/diameter/src/base/diameter_callback.erl b/lib/diameter/src/base/diameter_callback.erl
index f479cb6612..d04a416bef 100644
--- a/lib/diameter/src/base/diameter_callback.erl
+++ b/lib/diameter/src/base/diameter_callback.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,16 +26,16 @@
%% as the Diameter application callback in question. The record has
%% one field for each callback function as well as 'default' and
%% 'extra' fields. A function-specific field can be set to a
-%% diameter:evaluable() in order to redirect the callback
+%% diameter:eval() in order to redirect the callback
%% corresponding to that field, or to 'false' to request the default
%% callback implemented in this module. If neither of these fields are
%% set then the 'default' field determines the form of the callback: a
%% module name results in the usual callback as if the module had been
-%% configured directly as the callback module, a diameter_evaluable()
+%% configured directly as the callback module, a diameter_eval()
%% in a callback applied to the atom-valued callback name and argument
%% list. For all callbacks not to this module, the 'extra' field is a
%% list of additional arguments, following arguments supplied by
-%% diameter but preceeding those of the diameter:evaluable() being
+%% diameter but preceding those of the diameter:eval() being
%% applied.
%%
%% For example, the following config to diameter:start_service/2, in
diff --git a/lib/diameter/src/base/diameter_capx.erl b/lib/diameter/src/base/diameter_capx.erl
index 07a678c617..62b05644b2 100644
--- a/lib/diameter/src/base/diameter_capx.erl
+++ b/lib/diameter/src/base/diameter_capx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -94,6 +94,9 @@ recv_CER(CER, Svc, Dict) ->
recv_CEA(CEA, Svc, Dict) ->
try_it([fun rCEA/3, CEA, Svc, Dict]).
+-spec make_caps(#diameter_caps{}, [{atom(), term()}])
+ -> tried(#diameter_caps{}).
+
make_caps(Caps, Opts) ->
try_it([fun mk_caps/2, Caps, Opts]).
@@ -110,31 +113,20 @@ try_it([Fun | Args]) ->
%% mk_caps/2
mk_caps(Caps0, Opts) ->
- {Caps, _} = lists:foldl(fun set_cap/2,
- {Caps0, #diameter_caps{_ = false}},
- Opts),
- Caps.
-
--define(SC(K,F),
- set_cap({K, Val}, {Caps, #diameter_caps{F = false} = C}) ->
- {Caps#diameter_caps{F = cap(K, copy(Val))},
- C#diameter_caps{F = true}}).
-
-?SC('Origin-Host', origin_host);
-?SC('Origin-Realm', origin_realm);
-?SC('Host-IP-Address', host_ip_address);
-?SC('Vendor-Id', vendor_id);
-?SC('Product-Name', product_name);
-?SC('Origin-State-Id', origin_state_id);
-?SC('Supported-Vendor-Id', supported_vendor_id);
-?SC('Auth-Application-Id', auth_application_id);
-?SC('Inband-Security-Id', inband_security_id);
-?SC('Acct-Application-Id', acct_application_id);
-?SC('Vendor-Specific-Application-Id', vendor_specific_application_id);
-?SC('Firmware-Revision', firmware_revision);
-
-set_cap({Key, _}, _) ->
- ?THROW({duplicate, Key}).
+ Fields = diameter_gen_base_rfc3588:'#info-'(diameter_base_CER, fields),
+ Defs = lists:zip(Fields, tl(tuple_to_list(Caps0))),
+ Unset = maps:from_list([{F, true} || F <- lists:droplast(Fields)]), %% no 'AVP'
+ {Caps, _} = lists:foldl(fun set_cap/2, {Defs, Unset}, Opts),
+ #diameter_caps{} = list_to_tuple([diameter_caps | [V || {_,V} <- Caps]]).
+
+set_cap({F,V}, {Caps, Unset}) ->
+ case Unset of
+ #{F := true} ->
+ {lists:keyreplace(F, 1, Caps, {F, cap(F, copy(V))}),
+ maps:remove(F, Unset)};
+ _ ->
+ ?THROW({duplicate, F})
+ end.
cap(K, V)
when K == 'Origin-Host';
@@ -349,7 +341,7 @@ cs(LS, RS) ->
cea_from_cer(CER, Dict) ->
RecName = Dict:msg2rec('CEA'),
[_ | Values] = Dict:'#get-'(CER),
- Dict:'#set-'(Values, Dict:'#new-'(RecName)).
+ Dict:'#new-'([RecName | Values]).
%% rCEA/3
@@ -424,7 +416,48 @@ bcaps(N, Caps) ->
%% common_applications/3
%%
%% Identify the (local) applications to be supported on the connection
-%% in question.
+%% in question. The RFC says this:
+%%
+%% 2.4 Application Identifiers
+%%
+%% Relay and redirect agents MUST advertise the Relay Application ID,
+%% while all other Diameter nodes MUST advertise locally supported
+%% applications.
+%%
+%% Taken literally, every Diameter node should then advertise support
+%% for the Diameter common messages application, with id 0, since no
+%% node can perform capabilities exchange without it. Expecting this,
+%% or regarding the support as implicit, renders the Result-Code 5010
+%% (DIAMETER_NO_COMMON_APPLICATION) meaningless however, since every
+%% node would regard the common application as being in common with
+%% the peer. In practice, nodes may or may not advertise support for
+%% Diameter common messages.
+%%
+%% That only explicitly advertised applications should be considered
+%% when computing the intersection with the peer is supported here:
+%%
+%% 5.3. Capabilities Exchange
+%%
+%% The receiver of the Capabilities-Exchange-Request (CER) MUST
+%% determine common applications by computing the intersection of its
+%% own set of supported Application Ids against all of the
+%% Application-Id AVPs (Auth-Application-Id, Acct-Application-Id, and
+%% Vendor-Specific-Application-Id) present in the CER.
+%%
+%% The same section also has the following about capabilities exchange
+%% messages.
+%%
+%% The receiver only issues commands to its peers that have advertised
+%% support for the Diameter application that defines the command.
+%%
+%% This statement is also difficult to interpret literally since it
+%% would disallow D[WP]R and more when Diameter common messages isn't
+%% advertised. In practice, diameter lets requests be sent as long as
+%% there's a dictionary configured to support it, peer selection by
+%% advertised application being possible to preempt by passing
+%% candidate peers directly to diameter:call/4. The peer can always
+%% answer 3001 (DIAMETER_COMMAND_UNSUPPORTED) or 3007
+%% (DIAMETER_APPLICATION_UNSUPPORTED) if this is objectionable.
common_applications(LCaps, RCaps, #diameter_service{applications = Apps}) ->
LA = app_union(LCaps),
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 1ea5357924..493a6ab1e3 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,11 +20,8 @@
-module(diameter_codec).
--export([encode/2,
- decode/2,
- decode/3,
- setopts/1,
- getopt/1,
+-export([encode/2, encode/3,
+ decode/2, decode/3, decode/4,
collect_avps/1,
decode_header/1,
sequence_numbers/1,
@@ -32,14 +29,18 @@
msg_name/2,
msg_id/1]).
-%% Towards generated encoders (from diameter_gen.hrl).
--export([pack_avp/1,
+%% towards diameter_gen
+-export([pack_data/2,
pack_avp/2]).
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
--define(MASK(N,I), ((I) band (1 bsl (N)))).
+-define(PAD(Len), ((4 - (Len rem 4)) rem 4)).
+-define(BIT(B,I), (if B -> I; true -> 0 end)).
+-define(BIT(B), ?BIT(B,1)).
+-define(FLAGS(R,P,E,T), ?BIT(R):1, ?BIT(P):1, ?BIT(E):1, ?BIT(T):1, 0:4).
+-define(FLAG(B,D), (if is_boolean(B) -> B; true -> 0 /= (D) end)).
-type u32() :: 0..16#FFFFFFFF.
-type u24() :: 0..16#FFFFFF.
@@ -62,159 +63,132 @@
%% +-+-+-+-+-+-+-+-+-+-+-+-+-
%%% ---------------------------------------------------------------------------
-%%% # setopts/1
-%%% # getopt/1
+%%% # encode/2
%%% ---------------------------------------------------------------------------
-%% These functions are a compromise in the same vein as the use of the
-%% process dictionary in diameter_gen.hrl in generated codec modules.
-%% Instead of rewriting the entire dictionary generation to pass
-%% encode/decode options around, the calling process sets them by
-%% calling setopts/1. At current, the only option is whether or not to
-%% decode binaries as strings, which is used by diameter_types.
-
-setopts(Opts)
- when is_list(Opts) ->
- lists:foreach(fun setopt/1, Opts).
-
-%% The default string_decode true is for backwards compatibility.
-setopt({K, false = B})
- when K == string_decode;
- K == strict_mbit ->
- setopt(K, B);
-
-%% Regard anything but the generated RFC 3588 dictionary as modern.
-%% This affects the interpretation of defaults during the decode
-%% of values of type DiameterURI, this having changed from RFC 3588.
-%% (So much for backwards compatibility.)
-setopt({common_dictionary, diameter_gen_base_rfc3588}) ->
- setopt(rfc, 3588);
-
-setopt(_) ->
- ok.
-
-setopt(Key, Value) ->
- put({diameter, Key}, Value).
-
-getopt(Key) ->
- case get({diameter, Key}) of
- undefined when Key == string_decode;
- Key == strict_mbit ->
- true;
- undefined when Key == rfc ->
- 6733;
- V ->
- V
- end.
+%% The representative encode documented in diameter_codec(3). As of
+%% the options that affect encode (eg. ordered_encode), it's no longer
+%% *the* encode.
+
+encode(Mod, Msg) ->
+ encode(Mod, #{ordered_encode => true}, Msg).
%%% ---------------------------------------------------------------------------
-%%% # encode/2
+%%% # encode/3
%%% ---------------------------------------------------------------------------
--spec encode(module(), Msg :: term())
+-spec encode(module(),
+ map(),
+ Msg :: term())
-> #diameter_packet{}
| no_return().
-encode(Mod, #diameter_packet{} = Pkt) ->
+encode(Mod, Opts, #diameter_packet{} = Pkt) ->
try
- e(Mod, Pkt)
+ enc(Mod, Opts, Pkt)
catch
exit: {Reason, Stack, #diameter_header{} = H} = T ->
%% Exit with a header in the reason to let the caller
%% count encode errors.
?LOG(encode_error, {Reason, Stack, H}),
exit({?MODULE, encode, T});
- error: Reason ->
- T = {Reason, diameter_lib:get_stacktrace()},
+ error: Reason: Stack ->
+ T = {Reason, diameter_lib:stacktrace(Stack)},
?LOG(encode_error, T),
exit({?MODULE, encode, T})
end;
-encode(Mod, Msg) ->
+encode(Mod, Opts, Msg) ->
Seq = diameter_session:sequence(),
Hdr = #diameter_header{version = ?DIAMETER_VERSION,
end_to_end_id = Seq,
hop_by_hop_id = Seq},
- encode(Mod, #diameter_packet{header = Hdr,
- msg = Msg}).
+ encode(Mod, Opts, #diameter_packet{header = Hdr,
+ msg = Msg}).
+
+%% enc/3
-e(_, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) ->
- try encode_avps(reorder(As)) of
+enc(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]}
+ = Pkt) ->
+ try encode_avps(As, Opts) of
Avps ->
- Length = size(Avps) + 20,
+ Bin = list_to_binary(Avps),
+ Len = 20 + size(Bin),
#diameter_header{version = Vsn,
+ is_request = R,
+ is_proxiable = P,
+ is_error = E,
+ is_retransmitted = T,
cmd_code = Code,
application_id = Aid,
hop_by_hop_id = Hid,
end_to_end_id = Eid}
= Hdr,
- Flags = make_flags(0, Hdr),
-
Pkt#diameter_packet{header = Hdr,
- bin = <<Vsn:8, Length:24,
- Flags:8, Code:24,
+ bin = <<Vsn:8, Len:24,
+ ?FLAGS(R,P,E,T), Code:24,
Aid:32,
Hid:32,
Eid:32,
- Avps/binary>>}
+ Bin/binary>>}
catch
- error: Reason ->
- exit({Reason, diameter_lib:get_stacktrace(), Hdr})
+ error: Reason: Stack ->
+ exit({Reason, diameter_lib:stacktrace(Stack), Hdr})
end;
-e(Mod, #diameter_packet{header = Hdr0, msg = Msg} = Pkt) ->
+enc(Mod, Opts, #diameter_packet{header = Hdr0, msg = Msg} = Pkt) ->
+ MsgName = rec2msg(Mod, Msg),
+ {Code, Flags, Aid} = msg_header(Mod, MsgName, Hdr0),
+
#diameter_header{version = Vsn,
+ is_request = R,
+ is_proxiable = P,
+ is_error = E,
+ is_retransmitted = T,
hop_by_hop_id = Hid,
end_to_end_id = Eid}
= Hdr0,
- MsgName = rec2msg(Mod, Msg),
- {Code, Flags0, Aid} = msg_header(Mod, MsgName, Hdr0),
- Flags = make_flags(Flags0, Hdr0),
- Hdr = Hdr0#diameter_header{cmd_code = Code,
- application_id = Aid,
- is_request = 0 /= ?MASK(7, Flags),
- is_proxiable = 0 /= ?MASK(6, Flags),
- is_error = 0 /= ?MASK(5, Flags),
- is_retransmitted = 0 /= ?MASK(4, Flags)},
+ RB = ?FLAG(R, Flags band 2#10000000),
+ PB = ?FLAG(P, Flags band 2#01000000),
+ EB = ?FLAG(E, Flags band 2#00100000),
+ TB = ?FLAG(T, Flags band 2#00010000),
+
Values = values(Msg),
- try encode_avps(Mod, MsgName, Values) of
+ try encode_avps(Mod, MsgName, Values, Opts) of
Avps ->
- Length = size(Avps) + 20,
- Pkt#diameter_packet{header = Hdr#diameter_header{length = Length},
- bin = <<Vsn:8, Length:24,
- Flags:8, Code:24,
+ Bin = list_to_binary(Avps),
+ Len = 20 + size(Bin),
+
+ Hdr = Hdr0#diameter_header{length = Len,
+ cmd_code = Code,
+ application_id = Aid,
+ is_request = RB,
+ is_proxiable = PB,
+ is_error = EB,
+ is_retransmitted = TB},
+
+ Pkt#diameter_packet{header = Hdr,
+ bin = <<Vsn:8, Len:24,
+ ?FLAGS(RB, PB, EB, TB), Code:24,
Aid:32,
Hid:32,
Eid:32,
- Avps/binary>>}
+ Bin/binary>>}
catch
- error: Reason ->
- exit({Reason, diameter_lib:get_stacktrace(), Hdr})
+ error: Reason: Stack ->
+ Hdr = Hdr0#diameter_header{cmd_code = Code,
+ application_id = Aid,
+ is_request = RB,
+ is_proxiable = PB,
+ is_error = EB,
+ is_retransmitted = TB},
+ exit({Reason, diameter_lib:stacktrace(Stack), Hdr})
end.
-%% make_flags/2
-
-make_flags(Flags0, #diameter_header{is_request = R,
- is_proxiable = P,
- is_error = E,
- is_retransmitted = T}) ->
- {Flags, 3} = lists:foldl(fun(B,{F,N}) -> {mf(B,F,N), N-1} end,
- {Flags0, 7},
- [R,P,E,T]),
- Flags.
-
-mf(undefined, F, _) ->
- F;
-mf(B, F, N) -> %% reset the affected bit
- (F bxor (F band (1 bsl N))) bor bit(B, N).
-
-bit(true, N) -> 1 bsl N;
-bit(false, _) -> 0.
-
%% values/1
values([H|T])
@@ -223,7 +197,7 @@ values([H|T])
values(Avps) ->
Avps.
-%% encode_avps/3
+%% encode_avps/4
%% Specifying values as a #diameter_avp list bypasses arity and other
%% checks: the values are expected to be already encoded and the AVP's
@@ -231,56 +205,17 @@ values(Avps) ->
%% these have to be able to resend whatever comes.
%% Message as a list of #diameter_avp{} ...
-encode_avps(_, _, [#diameter_avp{} | _] = Avps) ->
- encode_avps(reorder(Avps));
+encode_avps(_, _, [#diameter_avp{} | _] = Avps, Opts) ->
+ encode_avps(Avps, Opts);
%% ... or as a tuple list or record.
-encode_avps(Mod, MsgName, Values) ->
- Mod:encode_avps(MsgName, Values).
+encode_avps(Mod, MsgName, Values, Opts) ->
+ Mod:encode_avps(MsgName, Values, Opts).
-%% reorder/1
-%%
-%% Reorder AVPs for the relay case using the index field of
-%% diameter_avp records. Decode populates this field in collect_avps
-%% and presents AVPs in reverse order. A relay then sends the reversed
-%% list with a Route-Record AVP prepended. The goal here is just to do
-%% lists:reverse/1 in Grouped AVPs and the outer list, but only in the
-%% case there are indexed AVPs at all, so as not to reverse lists that
-%% have been explicilty sent (unindexed, in the desired order) as a
-%% diameter_avp list. The effect is the same as lists:keysort/2, but
-%% only on the cases we expect, not a general sort.
-
-reorder(Avps) ->
- case reorder(Avps, []) of
- false ->
- Avps;
- Sorted ->
- Sorted
- end.
-
-%% reorder/3
-
-%% In case someone has reversed the list already. (Not likely.)
-reorder([#diameter_avp{index = 0} | _] = Avps, Acc) ->
- Avps ++ Acc;
-
-%% Assume indexed AVPs are in reverse order.
-reorder([#diameter_avp{index = N} = A | Avps], Acc)
- when is_integer(N) ->
- lists:reverse(Avps, [A | Acc]);
-
-%% An unindexed AVP.
-reorder([H | T], Acc) ->
- reorder(T, [H | Acc]);
-
-%% No indexed members.
-reorder([], _) ->
- false.
-
-%% encode_avps/1
+%% encode_avps/2
-encode_avps(Avps) ->
- list_to_binary(lists:map(fun pack_avp/1, Avps)).
+encode_avps(Avps, Opts) ->
+ [pack_avp(A, Opts) || A <- Avps].
%% msg_header/3
@@ -308,87 +243,104 @@ rec2msg(Mod, Rec) ->
%%% # decode/2
%%% ---------------------------------------------------------------------------
+%% The representative default decode documented in diameter_codec(3).
+%% As of the options that affect decode (eg. string_decode), it's no
+%% longer *the* decode.
+
+decode(Mod, Pkt) ->
+ Opts = #{decode_format => record,
+ string_decode => true,
+ strict_mbit => true,
+ rfc => 6733},
+ decode(Mod, Opts, Pkt).
+
+%%% ---------------------------------------------------------------------------
+%%% # decode/3
+%%% ---------------------------------------------------------------------------
+
%% Unsuccessfully decoded AVPs will be placed in #diameter_packet.errors.
--spec decode(module() | {module(), module()}, #diameter_packet{} | binary())
+-spec decode(module() | {module(), module()},
+ map(),
+ #diameter_packet{} | binary())
-> #diameter_packet{}.
%% An Answer setting the E-bit. The application dictionary is needed
-%% for the best-effort decode of Failed-AVP, and the best way to make
-%% this available to the AVP decode in diameter_gen.hrl, without
-%% having to rewrite the entire codec generation, is to place it in
-%% the process dictionary. It's the code in diameter_gen.hrl (that's
-%% included by every generated codec module) that looks for the entry.
-%% Not ideal, but it solves the problem relatively simply.
-decode({Mod, Mod}, Pkt) ->
- decode(Mod, Pkt);
-decode({Mod, AppMod}, Pkt) ->
- Key = {?MODULE, dictionary},
- put(Key, AppMod),
- try
- decode(Mod, Pkt)
- after
- erase(Key)
- end;
+%% for the best-effort decode of Failed-AVP.
+decode({Mod, AppMod}, Opts, Pkt) ->
+ decode(Mod, AppMod, Opts, Pkt);
%% Or not: a request, or an answer not setting the E-bit.
-decode(Mod, Pkt) ->
- decode(Mod:id(), Mod, Pkt).
+decode(Mod, Opts, Pkt) ->
+ decode(Mod, Mod, Opts, Pkt).
-%% decode/3
+%% decode/4
+
+decode(Id, Mod, Opts, Pkt)
+ when is_integer(Id) ->
+ decode(Id, Mod, Mod, Opts, Pkt);
+
+decode(Mod, AppMod, Opts, Pkt) ->
+ decode(Mod:id(), Mod, AppMod, Opts, Pkt).
+
+%% decode/5
%% Relay application: just extract the avp's without any decoding of
%% their data since we don't know the application in question.
-decode(?APP_ID_RELAY, _, #diameter_packet{} = Pkt) ->
- case collect_avps(Pkt) of
- {E, As} ->
- Pkt#diameter_packet{avps = As,
- errors = [E]};
- As ->
- Pkt#diameter_packet{avps = As}
- end;
+decode(?APP_ID_RELAY, _, _, _, #diameter_packet{} = Pkt) ->
+ collect_avps(Pkt);
%% Otherwise decode using the dictionary.
-decode(_, Mod, #diameter_packet{header = Hdr} = Pkt) ->
+decode(_, Mod, AppMod, Opts, #diameter_packet{header = Hdr} = Pkt) ->
#diameter_header{cmd_code = CmdCode,
is_request = IsRequest,
is_error = IsError}
= Hdr,
- MsgName = if IsError andalso not IsRequest ->
+ MsgName = if IsError, not IsRequest ->
'answer-message';
true ->
Mod:msg_name(CmdCode, IsRequest)
end,
- decode_avps(MsgName, Mod, Pkt, collect_avps(Pkt));
+ decode_avps(MsgName, Mod, AppMod, Opts, Pkt);
-decode(Id, Mod, Bin)
+decode(Id, Mod, AppMod, Opts, Bin)
when is_binary(Bin) ->
- decode(Id, Mod, #diameter_packet{header = decode_header(Bin), bin = Bin}).
-
-%% decode_avps/4
+ decode(Id, Mod, AppMod, Opts, #diameter_packet{header = decode_header(Bin),
+ bin = Bin}).
-decode_avps(MsgName, Mod, Pkt, {E, Avps}) ->
- ?LOG(invalid_avp_length, Pkt#diameter_packet.header),
- #diameter_packet{errors = Failed}
- = P
- = decode_avps(MsgName, Mod, Pkt, Avps),
- P#diameter_packet{errors = [E | Failed]};
+%% decode_avps/5
-decode_avps('', _, Pkt, Avps) -> %% unknown message ...
- ?LOG(unknown_message, Pkt#diameter_packet.header),
- Pkt#diameter_packet{avps = lists:reverse(Avps),
- errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED
+decode_avps('', _, _, _, #diameter_packet{header = H, %% unknown message
+ bin = Bin}
+ = Pkt) ->
+ ?LOG(unknown_message, H),
+ Pkt#diameter_packet{avps = collect_avps(Bin),
+ errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED
%% msg = undefined identifies this case.
-decode_avps(MsgName, Mod, Pkt, Avps) -> %% ... or not
- {Rec, As, Errors} = Mod:decode_avps(MsgName, Avps),
+decode_avps(MsgName, Mod, AppMod, Opts, #diameter_packet{bin = Bin} = Pkt) ->
+ {_, Avps} = split_binary(Bin, 20),
+ {Rec, As, Errors} = Mod:decode_avps(MsgName,
+ Avps,
+ Opts#{app_dictionary => AppMod,
+ failed_avp => false}),
?LOGC([] /= Errors, decode_errors, Pkt#diameter_packet.header),
- Pkt#diameter_packet{msg = Rec,
+ Pkt#diameter_packet{msg = reformat(MsgName, Rec, Opts),
errors = Errors,
avps = As}.
+%% reformat/3
+
+reformat(MsgName, Avps, #{decode_format := T})
+ when T == map;
+ T == list ->
+ [MsgName | Avps];
+
+reformat(_, Msg, _) ->
+ Msg.
+
%%% ---------------------------------------------------------------------------
%%% # decode_header/1
%%% ---------------------------------------------------------------------------
@@ -399,14 +351,12 @@ decode_avps(MsgName, Mod, Pkt, Avps) -> %% ... or not
decode_header(<<Version:8,
MsgLength:24,
- CmdFlags:1/binary,
+ R:1, P:1, E:1, T:1, _:4,
CmdCode:24,
ApplicationId:32,
HopByHopId:32,
EndToEndId:32,
_/binary>>) ->
- <<R:1, P:1, E:1, T:1, _:4>>
- = CmdFlags,
%% 3588 (ch 3) says that reserved bits MUST be set to 0 and ignored
%% by the receiver.
@@ -518,7 +468,7 @@ msg_id(#diameter_packet{header = #diameter_header{} = Hdr}) ->
msg_id(#diameter_header{application_id = A,
cmd_code = C,
is_request = R}) ->
- {A, C, if R -> 1; true -> 0 end};
+ {A, C, ?BIT(R)};
msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/binary>>) ->
{ApplId, CmdCode, Rbit}.
@@ -527,34 +477,21 @@ msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/binary>>) ->
%%% # collect_avps/1
%%% ---------------------------------------------------------------------------
-%% Note that the returned list of AVP's is reversed relative to their
-%% order in the binary. Note also that grouped avp's aren't unraveled,
-%% only those at the top level.
+%% This is only used for the relay decode. Note that grouped avp's
+%% aren't unraveled, only those at the top level.
--spec collect_avps(#diameter_packet{} | binary())
- -> [Avp]
- | {Error, [Avp]}
- when Avp :: #diameter_avp{},
- Error :: {5014, #diameter_avp{}}.
+-spec collect_avps(#diameter_packet{})
+ -> #diameter_packet{};
+ (binary())
+ -> [#diameter_avp{}].
-collect_avps(#diameter_packet{bin = Bin}) ->
- <<_:20/binary, Avps/binary>> = Bin,
- collect_avps(Avps);
+collect_avps(#diameter_packet{bin = Bin} = Pkt) ->
+ Pkt#diameter_packet{avps = collect_avps(Bin)};
-collect_avps(Bin)
- when is_binary(Bin) ->
- collect_avps(Bin, 0, []).
-
-collect_avps(<<>>, _, Acc) ->
- Acc;
-collect_avps(Bin, N, Acc) ->
- try split_avp(Bin) of
- {Rest, AVP} ->
- collect_avps(Rest, N+1, [AVP#diameter_avp{index = N} | Acc])
- catch
- ?FAILURE(Error) ->
- {Error, Acc}
- end.
+collect_avps(<<_:20/binary, Avps/binary>>) ->
+ collect(Avps).
+
+%% collect/1
%% 0 1 2 3
%% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@@ -568,32 +505,47 @@ collect_avps(Bin, N, Acc) ->
%% | Data ...
%% +-+-+-+-+-+-+-+-+
-%% split_avp/1
-
-split_avp(Bin) ->
- {Code, V, M, P, Len, HdrLen} = split_head(Bin),
-
- <<_:HdrLen/binary, Rest/binary>> = Bin,
- {Data, B} = split_data(Rest, Len - HdrLen),
-
- {B, #diameter_avp{code = Code,
- vendor_id = V,
- is_mandatory = 1 == M,
- need_encryption = 1 == P,
- data = Data}}.
+collect(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>) ->
+ collect(Rest,
+ Code,
+ if 1 == V -> I; 0 == V -> undefined end,
+ Len - 8 - V*4, %% Might be negative, which ensures
+ ?PAD(Len), %% failure of the match below.
+ 1 == M,
+ 1 == P);
-%% split_head/1
+collect(<<>>) ->
+ [];
-split_head(<<Code:32, 1:1, M:1, P:1, _:5, Len:24, V:32, _/binary>>) ->
- {Code, V, M, P, Len, 12};
+%% Header is truncated. pack_avp/1 will pad this at encode if sent in
+%% a Failed-AVP.
+collect(Bin) ->
+ [#diameter_avp{data = {5014, Bin}}].
-split_head(<<Code:32, 0:1, M:1, P:1, _:5, Len:24, _/binary>>) ->
- {Code, undefined, M, P, Len, 8};
+%% collect/7
-%% Header is truncated.
-split_head(Bin) ->
- ?THROW({5014, #diameter_avp{data = Bin}}).
-%% Note that pack_avp/1 will pad this at encode if sent in a Failed-AVP.
+collect(Bin, Code, Vid, DataLen, Pad, M, P) ->
+ case Bin of
+ <<Data:DataLen/binary, _:Pad/binary, Rest/binary>> ->
+ Avp = #diameter_avp{code = Code,
+ vendor_id = Vid,
+ is_mandatory = M,
+ need_encryption = P,
+ data = Data},
+ [Avp | collect(Rest)];
+ _ ->
+ %% Length in header points past the end of the message, or
+ %% doesn't span the header. Note that an length error can
+ %% only occur in the trailing AVP of a message or Grouped
+ %% AVP, since a faulty AVP Length is otherwise
+ %% indistinguishable from a correct one here, as we don't
+ %% know the types of the AVPs being extracted.
+ [#diameter_avp{code = Code,
+ vendor_id = Vid,
+ is_mandatory = M,
+ need_encryption = P,
+ data = {5014, Bin}}]
+ end.
%% 3588:
%%
@@ -626,35 +578,8 @@ split_head(Bin) ->
%% the minimum value mean we might not know the identity of the AVP and
%% (2) the last sentence covers this case.
-%% split_data/3
-
-split_data(Bin, Len) ->
- Pad = (4 - (Len rem 4)) rem 4,
-
- %% Len might be negative here, but that ensures the failure of the
- %% binary match.
-
- case Bin of
- <<Data:Len/binary, _:Pad/binary, Rest/binary>> ->
- {Data, Rest};
- _ ->
- %% Header length points past the end of the message, or
- %% doesn't span the header. As stated in the 6733 text
- %% above, it's sufficient to return a zero-filled minimal
- %% payload if this is a request. Do this (in cases that we
- %% know the type) by inducing a decode failure and letting
- %% the dictionary's decode (in diameter_gen) deal with it.
- %%
- %% Note that the extra bit can only occur in the trailing
- %% AVP of a message or Grouped AVP, since a faulty AVP
- %% Length is otherwise indistinguishable from a correct
- %% one here, since we don't know the types of the AVPs
- %% being extracted.
- {<<0:1, Bin/binary>>, <<>>}
- end.
-
%%% ---------------------------------------------------------------------------
-%%% # pack_avp/1
+%%% # pack_avp/2
%%% ---------------------------------------------------------------------------
%% The normal case here is data as an #diameter_avp{} list or an
@@ -664,104 +589,96 @@ split_data(Bin, Len) ->
%% Decoded Grouped AVP with decoded components: ignore components
%% since they're already encoded in the Grouped AVP.
-pack_avp([#diameter_avp{} = Grouped | _Components]) ->
- pack_avp(Grouped);
+pack_avp([#diameter_avp{} = Grouped | _Components], Opts) ->
+ pack_avp(Grouped, Opts);
%% Grouped AVP whose components need packing. It's intentional that
%% this isn't equivalent to [Grouped | Components]: here the
%% components need to be encoded before wrapping with the Grouped AVP,
%% and the list is flat, nesting being accomplished in the data
%% fields.
-pack_avp(#diameter_avp{data = [#diameter_avp{} | _] = Components} = Grouped) ->
- pack_avp(Grouped#diameter_avp{data = encode_avps(Components)});
+pack_avp(#diameter_avp{data = [#diameter_avp{} | _] = Components}
+ = Grouped,
+ Opts) ->
+ pack_data(Grouped, encode_avps(Components, Opts));
%% Data as a type/value tuple ...
-pack_avp(#diameter_avp{data = {Type, Value}} = A)
+pack_avp(#diameter_avp{data = {Type, Value}} = A, Opts)
when is_atom(Type) ->
- pack_avp(A#diameter_avp{data = diameter_types:Type(encode, Value)});
+ pack_data(A, diameter_types:Type(encode, Value, Opts));
%% ... with a header in various forms ...
-pack_avp(#diameter_avp{data = {{_,_,_} = T, {Type, Value}}}) ->
- pack_avp(T, iolist_to_binary(diameter_types:Type(encode, Value)));
+pack_avp(#diameter_avp{data = {T, {Type, Value}}}, Opts) ->
+ pack_data(T, diameter_types:Type(encode, Value, Opts));
-pack_avp(#diameter_avp{data = {{_,_,_} = T, Bin}})
- when is_binary(Bin) ->
- pack_avp(T, Bin);
+pack_avp(#diameter_avp{data = {T, Data}}, _) ->
+ pack_data(T, Data);
-pack_avp(#diameter_avp{data = {Dict, Name, Value}} = A) ->
- {Code, _Flags, Vid} = Hdr = Dict:avp_header(Name),
- {Name, Type} = Dict:avp_name(Code, Vid),
- pack_avp(A#diameter_avp{data = {Hdr, {Type, Value}}});
+pack_avp(#diameter_avp{data = {Dict, Name, Value}}, Opts) ->
+ pack_data(Dict:avp_header(Name), Dict:avp(encode, Value, Name, Opts));
%% ... with a truncated header ...
-pack_avp(#diameter_avp{code = undefined, data = B})
+pack_avp(#diameter_avp{code = undefined, data = B}, _)
when is_binary(B) ->
%% Reset the AVP Length of an AVP Header resulting from a 5014
%% error. The RFC doesn't explicitly say to do this but the
%% receiver can't correctly extract this and following AVP's
%% without a correct length. On the downside, the header doesn't
- %% reveal if the received header has been padded.
- Pad = 8*header_length(B) - bit_size(B),
- Len = size(<<H:5/binary, _:24, T/binary>> = <<B/binary, 0:Pad>>),
- <<H/binary, Len:24, T/binary>>;
-
-%% ... when ignoring errors in Failed-AVP ...
-%% ... during a relay encode ...
-pack_avp(#diameter_avp{data = <<0:1, B/binary>>} = A) ->
- pack_avp(A#diameter_avp{data = B});
-
-%% ... or as an iolist.
-pack_avp(#diameter_avp{code = Code,
- vendor_id = V,
- is_mandatory = M,
- need_encryption = P,
- data = Data}) ->
- Flags = lists:foldl(fun flag_avp/2, 0, [{V /= undefined, 2#10000000},
- {M, 2#01000000},
- {P, 2#00100000}]),
- pack_avp({Code, Flags, V}, iolist_to_binary(Data)).
-
-header_length(<<_:32, 1:1, _/bitstring>>) ->
+ %% reveal if the received header has been padded. Discard bytes
+ %% from the length header for this reason, to avoid creating a sub
+ %% binary for no useful reason.
+ Len = header_length(B),
+ Sz = min(5, size(B)),
+ <<B:Sz/binary, 0:(5-Sz)/unit:8, Len:24, 0:(Len-8)/unit:8>>;
+
+%% Ignoring errors in Failed-AVP or during a relay encode.
+pack_avp(#diameter_avp{data = {5014, Data}} = A, _) ->
+ pack_data(A, Data);
+
+pack_avp(#diameter_avp{data = Data} = A, _) ->
+ pack_data(A, Data).
+
+header_length(<<_:32, 1:1, _/bits>>) ->
12;
header_length(_) ->
8.
-flag_avp({true, B}, F) ->
- F bor B;
-flag_avp({false, _}, F) ->
- F.
-
%%% ---------------------------------------------------------------------------
-%%% # pack_avp/2
+%%% # pack_data/2
%%% ---------------------------------------------------------------------------
-pack_avp({Code, Flags, VendorId}, Bin)
- when is_binary(Bin) ->
- Sz = size(Bin),
- pack_avp(Code, Flags, VendorId, Sz, pad(Sz rem 4, Bin)).
-
-pad(0, Bin) ->
- Bin;
-pad(N, Bin) ->
- P = 8*(4-N),
- <<Bin/binary, 0:P>>.
-%% Note that padding is not included in the length field as mandated by
-%% the RFC.
-
-%% pack_avp/5
+pack_data(#diameter_avp{code = Code,
+ vendor_id = V,
+ is_mandatory = M,
+ need_encryption = P},
+ Data) ->
+ Flags = ?BIT(V /= undefined, 2#10000000)
+ bor ?BIT(M, 2#01000000)
+ bor ?BIT(P, 2#00100000),
+ pack(Code, Flags, V, Data);
+
+pack_data({Code, Flags, VendorId}, Data) ->
+ pack(Code, Flags, VendorId, Data).
+
+%% pack/4
+
+pack(Code, Flags, VendorId, Data) ->
+ Sz = iolist_size(Data),
+ pack(Code, Flags, Sz, VendorId, Data, ?PAD(Sz)).
+%% Padding is not included in the length field, as mandated by the RFC.
+
+%% pack/6
%%
%% Prepend the vendor id as required.
-pack_avp(Code, Flags, Vid, Sz, Bin)
+pack(Code, Flags, Sz, _Vid, Data, Pad)
when 0 == Flags band 2#10000000 ->
- undefined = Vid, %% sanity check
- pack_avp(Code, Flags, Sz, Bin);
+ pack(Code, Flags, Sz, 0, 0, Data, Pad);
-pack_avp(Code, Flags, Vid, Sz, Bin) ->
- pack_avp(Code, Flags, Sz+4, <<Vid:32, Bin/binary>>).
+pack(Code, Flags, Sz, Vid, Data, Pad) ->
+ pack(Code, Flags, Sz+4, Vid, 1, Data, Pad).
-%% pack_avp/4
+%% pack/7
-pack_avp(Code, Flags, Sz, Bin) ->
- Length = Sz + 8,
- <<Code:32, Flags:8, Length:24, Bin/binary>>.
+pack(Code, Flags, Sz, VId, V, Data, Pad) ->
+ [<<Code:32, Flags:8, (8+Sz):24, VId:V/unit:32>>, Data, <<0:Pad/unit:8>>].
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 245a3ea7ac..36ae4c2276 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -102,9 +102,6 @@
-record(monitor, {mref = make_ref() :: reference(),
service}). %% name
-%% The default sequence mask.
--define(NOMASK, {0,32}).
-
%% Time to lay low before restarting a dead service.
-define(RESTART_SLEEP, 2000).
@@ -277,7 +274,7 @@ start_link() ->
start_link(T) ->
proc_lib:start_link(?MODULE, init, [T], infinity, []).
-
+
state() ->
call(state).
@@ -535,12 +532,12 @@ stop(SvcName) ->
%% restrict applications so that that there's one while the service
%% has many.
-add(SvcName, Type, Opts) ->
+add(SvcName, Type, Opts0) ->
%% 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),
+ Opts = transport_opts(Opts0),
Ref = make_ref(),
true = diameter_reg:add_new(?TRANSPORT_KEY(Ref)),
@@ -560,70 +557,186 @@ add(SvcName, Type, Opts) ->
end.
transport_opts(Opts) ->
- lists:foreach(fun(T) -> opt(T) orelse ?THROW({invalid, T}) end, Opts).
+ [setopt(transport, T) || T <- Opts].
+
+%% setopt/2
+
+setopt(K, T) ->
+ case opt(K, T) of
+ {value, X} ->
+ X;
+ true ->
+ T;
+ false ->
+ ?THROW({invalid, T});
+ {error, Reason} ->
+ ?THROW({invalid, T, Reason})
+ end.
+
+%% opt/2
+
+opt(_, {incoming_maxlen, N}) ->
+ is_integer(N) andalso 0 =< N andalso N < 1 bsl 24;
+
+opt(service, {K, B})
+ when K == string_decode;
+ K == traffic_counters ->
+ is_boolean(B);
+
+opt(service, {K, false})
+ when K == share_peers;
+ K == use_shared_peers;
+ K == monitor;
+ K == restrict_connections;
+ K == strict_arities ->
+ true;
+
+opt(service, {K, true})
+ when K == share_peers;
+ K == use_shared_peers;
+ K == strict_arities ->
+ true;
+
+opt(service, {decode_format, T})
+ when T == record;
+ T == list;
+ T == map;
+ T == none;
+ T == record_from_map ->
+ true;
+
+opt(service, {strict_arities, T})
+ when T == encode;
+ T == decode ->
+ true;
-opt({transport_module, M}) ->
+opt(service, {restrict_connections, T})
+ when T == node;
+ T == nodes ->
+ true;
+
+opt(service, {K, T})
+ when (K == share_peers
+ orelse K == use_shared_peers
+ orelse K == restrict_connections), ([] == T
+ orelse is_atom(hd(T))) ->
+ true;
+
+opt(service, {monitor, P}) ->
+ is_pid(P);
+
+opt(service, {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 ->
+ is_list(Nodes) orelse {error, Nodes}
+ catch
+ E:R:Stack ->
+ {error, {E, R, Stack}}
+ end;
+
+opt(service, {sequence, {H,N}}) ->
+ 0 =< N andalso N =< 32
+ andalso is_integer(H)
+ andalso 0 =< H
+ andalso 0 == H bsr (32-N);
+
+opt(service = S, {sequence = K, F}) ->
+ try diameter_lib:eval(F) of
+ {_,_} = T ->
+ KT = {K,T},
+ opt(S, KT) andalso {value, KT};
+ V ->
+ {error, V}
+ catch
+ E:R:Stack ->
+ {error, {E, R, Stack}}
+ end;
+
+opt(transport, {transport_module, M}) ->
is_atom(M);
-opt({transport_config, _, Tmo}) ->
+opt(transport, {transport_config, _, Tmo}) ->
?IS_UINT32(Tmo) orelse Tmo == infinity;
-opt({applications, As}) ->
+opt(transport, {applications, As}) ->
is_list(As);
-opt({capabilities, Os}) ->
- is_list(Os) andalso ok == encode_CER(Os);
+opt(transport, {capabilities, Os}) ->
+ is_list(Os) andalso try ok = encode_CER(Os), true
+ catch ?FAILURE(No) -> {error, No}
+ end;
-opt({K, Tmo})
+opt(_, {K, Tmo})
when K == capx_timeout;
K == dpr_timeout;
K == dpa_timeout ->
?IS_UINT32(Tmo);
-opt({capx_strictness, B}) ->
+opt(_, {capx_strictness, B}) ->
+ is_boolean(B) andalso {value, {strict_capx, B}};
+opt(_, {K, B})
+ when K == strict_capx;
+ K == strict_mbit ->
is_boolean(B);
-opt({length_errors, T}) ->
+opt(_, {avp_dictionaries, Mods}) ->
+ is_list(Mods) andalso lists:all(fun erlang:is_atom/1, Mods);
+
+opt(_, {length_errors, T}) ->
lists:member(T, [exit, handle, discard]);
-opt({K, Tmo})
- when K == reconnect_timer; %% deprecated
- K == connect_timer ->
+opt(transport, {reconnect_timer, Tmo}) -> %% deprecated
+ ?IS_UINT32(Tmo) andalso {value, {connect_timer, Tmo}};
+opt(_, {connect_timer, Tmo}) ->
?IS_UINT32(Tmo);
-opt({watchdog_timer, {M,F,A}})
+opt(_, {watchdog_timer, {M,F,A}})
when is_atom(M), is_atom(F), is_list(A) ->
true;
-opt({watchdog_timer, Tmo}) ->
+opt(_, {watchdog_timer, Tmo}) ->
?IS_UINT32(Tmo);
-opt({watchdog_config, L}) ->
- is_list(L) andalso lists:all(fun wdopt/1, L);
+opt(_, {watchdog_config, L}) ->
+ is_list(L) andalso lists:all(fun wd/1, L);
-opt({spawn_opt, Opts}) ->
- is_list(Opts);
+opt(_, {spawn_opt, {M,F,A}})
+ when is_atom(M), is_atom(F), is_list(A) ->
+ true;
+opt(_, {spawn_opt = K, Opts}) ->
+ if is_list(Opts) ->
+ {value, {K, spawn_opts(Opts)}};
+ true ->
+ false
+ end;
-opt({pool_size, N}) ->
+opt(_, {pool_size, N}) ->
is_integer(N) andalso 0 < N;
-%% Options that we can't validate.
-opt({K, _})
+%% Options we can't validate.
+opt(_, {K, _})
+ when K == disconnect_cb;
+ K == capabilities_cb ->
+ true;
+opt(transport, {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.
+%% Anything else, which is ignored in transport config. This makes
+%% options sensitive to spelling mistakes, but arbitrary options are
+%% passed by some users as a way to identify transports so can't just
+%% do away with it.
+opt(K, _) ->
+ K == transport.
+
+%% wd/1
-wdopt({K,N}) ->
+wd({K,N}) ->
(K == okay orelse K == suspect) andalso is_integer(N) andalso 0 =< N;
-wdopt(_) ->
+wd(_) ->
false.
%% start_transport/2
@@ -676,7 +789,7 @@ stop_transport(SvcName, Refs) ->
make_config(SvcName, Opts) ->
AppOpts = [T || {application, _} = T <- Opts],
- Apps = init_apps(AppOpts),
+ Apps = [init_app(T) || T <- AppOpts],
[] == Apps andalso ?THROW(no_apps),
@@ -688,16 +801,7 @@ make_config(SvcName, Opts) ->
ok = encode_CER(CapOpts),
- SvcOpts = make_opts((Opts -- AppOpts) -- CapOpts,
- [{false, share_peers},
- {false, use_shared_peers},
- {false, monitor},
- {?NOMASK, sequence},
- {nodes, restrict_connections},
- {16#FFFFFF, incoming_maxlen},
- {true, strict_mbit},
- {true, string_decode},
- {[], spawn_opt}]),
+ SvcOpts = service_opts((Opts -- AppOpts) -- CapOpts),
D = proplists:get_value(string_decode, SvcOpts, true),
@@ -711,90 +815,21 @@ binary_caps(Caps, true) ->
binary_caps(Caps, false) ->
diameter_capx:binary_caps(Caps).
-%% make_opts/2
-
-make_opts(Opts, Defs) ->
- Known = [{K, get_opt(K, Opts, D)} || {D,K} <- Defs],
- Unknown = Opts -- Known,
+%% service_opts/1
- [] == Unknown orelse ?THROW({invalid, hd(Unknown)}),
+service_opts(Opts) ->
+ Res = [setopt(service, T) || T <- Opts],
+ Keys = sets:to_list(sets:from_list([K || {K,_} <- Res])), %% unique
+ Dups = lists:foldl(fun(K,A) -> lists:keydelete(K, 1, A) end, Res, Keys),
+ [] == Dups orelse ?THROW({duplicate, Dups}),
+ Res.
+%% Reject duplicates on a service, but not on a transport. There's no
+%% particular reason for the inconsistency, but the historic behaviour
+%% ignores all but the first of a transport_opt(), and there's no real
+%% reason to change it.
- [{K, opt(K,V)} || {K,V} <- Known].
-
-opt(incoming_maxlen, N)
- when 0 =< N, N < 1 bsl 24 ->
- N;
-
-opt(spawn_opt, L)
- when is_list(L) ->
- L;
-
-opt(K, false = B)
- when K == share_peers;
- K == use_shared_peers;
- K == monitor;
- K == restrict_connections;
- K == strict_mbit;
- K == string_decode ->
- B;
-
-opt(K, true = B)
- when K == share_peers;
- K == use_shared_peers;
- K == strict_mbit;
- K == string_decode ->
- B;
-
-opt(restrict_connections, T)
- when T == node;
- 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(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 ->
- ?THROW({value, {K,V}})
- catch
- E:R ->
- ?THROW({value, {K, E, R, ?STACK}})
- end;
-
-opt(sequence, {_,_} = T) ->
- sequence(T);
-
-opt(sequence = K, F) ->
- try diameter_lib:eval(F) of
- T -> sequence(T)
- catch
- E:R ->
- ?THROW({value, {K, E, R, ?STACK}})
- end;
-
-opt(K, _) ->
- ?THROW({value, K}).
-
-sequence({H,N} = T)
- when 0 =< N, N =< 32, 0 =< H, 0 == H bsr (32-N) ->
- T;
-
-sequence(_) ->
- ?THROW({value, sequence}).
+spawn_opts(L) ->
+ [T || T <- L, T /= link, T /= monitor].
make_caps(Caps, Opts) ->
case diameter_capx:make_caps(Caps, Opts) of
@@ -822,10 +857,7 @@ encode_CER(Opts) ->
?THROW(Reason)
end.
-init_apps(Opts) ->
- lists:foldl(fun app_acc/2, [], lists:reverse(Opts)).
-
-app_acc({application, Opts} = T, Acc) ->
+init_app({application, Opts} = T) ->
is_list(Opts) orelse ?THROW(T),
[Dict, Mod] = get_opt([dictionary, module], Opts),
@@ -834,15 +866,14 @@ app_acc({application, Opts} = T, Acc) ->
M = get_opt(call_mutates_state, Opts, false, [true]),
A = get_opt(answer_errors, Opts, discard, [callback, report]),
P = get_opt(request_errors, Opts, answer_3xxx, [answer, callback]),
- [#diameter_app{alias = Alias,
- dictionary = Dict,
- id = cb(Dict, id),
- module = init_mod(Mod),
- init_state = ModS,
- mutable = M,
- options = [{answer_errors, A},
- {request_errors, P}]}
- | Acc].
+ #diameter_app{alias = Alias,
+ dictionary = Dict,
+ id = cb(Dict, id),
+ module = init_mod(Mod),
+ init_state = ModS,
+ mutable = M,
+ options = [{answer_errors, A},
+ {request_errors, P}]}.
init_mod(#diameter_callback{} = R) ->
init_mod([diameter_callback, R]);
@@ -868,7 +899,7 @@ init_cb(List) ->
V <- [proplists:get_value(F, List, D)]],
#diameter_callback{} = list_to_tuple([diameter_callback | Values]).
-%% Retreive and validate.
+%% Retrieve and validate.
get_opt(Key, List, Def, Other) ->
init_opt(Key, get_opt(Key, List, Def), [Def|Other]).
@@ -901,8 +932,8 @@ cb(M,F) ->
try M:F() of
V -> V
catch
- E: Reason ->
- ?THROW({callback, E, Reason, ?STACK})
+ E: Reason: Stack ->
+ ?THROW({callback, E, Reason, Stack})
end.
%% call/1
diff --git a/lib/diameter/src/base/diameter_dict.erl b/lib/diameter/src/base/diameter_dict.erl
deleted file mode 100644
index 7db294a1b1..0000000000
--- a/lib/diameter/src/base/diameter_dict.erl
+++ /dev/null
@@ -1,154 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%% This module provide OTP's dict interface built on top of ets.
-%%
-%% Note that while the interface is the same as dict the semantics
-%% aren't quite. A Dict here is just a table identifier (although
-%% this fact can't be used if you want dict/ets-based implementations
-%% to be interchangeable) so changes made to the Dict modify the
-%% underlying table. For merge/3, the first argument table is modified.
-%%
-%% The underlying ets table implementing a dict is deleted when the
-%% process from which new() was invoked exits and the dict is only
-%% writable from this process.
-%%
-%% The reason for this is to be able to swap dict/ets-based
-%% implementations: the former is easier to debug, the latter is
-%% faster for larger tables. It's also just a nice interface even
-%% when there's no need for swapability.
-%%
-
--module(diameter_dict).
-
--export([append/3,
- append_list/3,
- erase/2,
- fetch/2,
- fetch_keys/1,
- filter/2,
- find/2,
- fold/3,
- from_list/1,
- is_key/2,
- map/2,
- merge/3,
- new/0,
- store/3,
- to_list/1,
- update/3,
- update/4,
- update_counter/3]).
-
-%%% ----------------------------------------------------------
-%%% EXPORTED INTERNAL FUNCTIONS
-%%% ----------------------------------------------------------
-
-append(Key, Value, Dict) ->
- append_list(Key, [Value], Dict).
-
-append_list(Key, ValueList, Dict)
- when is_list(ValueList) ->
- update(Key, fun(V) -> V ++ ValueList end, ValueList, Dict).
-
-erase(Key, Dict) ->
- ets:delete(Dict, Key),
- Dict.
-
-fetch(Key, Dict) ->
- {ok, V} = find(Key, Dict),
- V.
-
-fetch_keys(Dict) ->
- ets:foldl(fun({K,_}, Acc) -> [K | Acc] end, [], Dict).
-
-filter(Pred, Dict) ->
- lists:foreach(fun({K,V}) -> filter(Pred(K,V), K, Dict) end, to_list(Dict)),
- Dict.
-
-find(Key, Dict) ->
- case ets:lookup(Dict, Key) of
- [{Key, V}] ->
- {ok, V};
- [] ->
- error
- end.
-
-fold(Fun, Acc0, Dict) ->
- ets:foldl(fun({K,V}, Acc) -> Fun(K, V, Acc) end, Acc0, Dict).
-
-from_list(List) ->
- lists:foldl(fun store/2, new(), List).
-
-is_key(Key, Dict) ->
- ets:member(Dict, Key).
-
-map(Fun, Dict) ->
- lists:foreach(fun({K,V}) -> store(K, Fun(K,V), Dict) end, to_list(Dict)),
- Dict.
-
-merge(Fun, Dict1, Dict2) ->
- fold(fun(K2,V2,_) ->
- update(K2, fun(V1) -> Fun(K2, V1, V2) end, V2, Dict1)
- end,
- Dict1,
- Dict2).
-
-new() ->
- ets:new(?MODULE, [set]).
-
-store(Key, Value, Dict) ->
- store({Key, Value}, Dict).
-
-to_list(Dict) ->
- ets:tab2list(Dict).
-
-update(Key, Fun, Dict) ->
- store(Key, Fun(fetch(Key, Dict)), Dict).
-
-update(Key, Fun, Initial, Dict) ->
- store(Key, map(Key, Fun, Dict, Initial), Dict).
-
-update_counter(Key, Increment, Dict)
- when is_integer(Increment) ->
- update(Key, fun(V) -> V + Increment end, Increment, Dict).
-
-%%% ---------------------------------------------------------
-%%% INTERNAL FUNCTIONS
-%%% ---------------------------------------------------------
-
-store({_,_} = T, Dict) ->
- ets:insert(Dict, T),
- Dict.
-
-filter(true, _, _) ->
- ok;
-filter(false, K, Dict) ->
- erase(K, Dict).
-
-map(Key, Fun, Dict, Error) ->
- case find(Key, Dict) of
- {ok, V} ->
- Fun(V);
- error ->
- Error
- end.
-
diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl
new file mode 100644
index 0000000000..d110a3015e
--- /dev/null
+++ b/lib/diameter/src/base/diameter_gen.erl
@@ -0,0 +1,951 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% This file contains code that encode/decode modules generated by
+%% diameter_codegen.erl calls to implement the functionality. This
+%% code does most of the work, the generated code being kept simple.
+%%
+
+-module(diameter_gen).
+
+-compile({inline, [incr/8,
+ incr/4,
+ field/1,
+ setopts/4,
+ avp_arity/5,
+ set_failed/2,
+ set_strict/3]}).
+
+-export([encode_avps/3,
+ decode_avps/3,
+ grouped_avp/4,
+ empty_group/2,
+ empty/2]).
+
+-include_lib("diameter/include/diameter.hrl").
+
+-define(THROW(T), throw({?MODULE, T})).
+
+-type parent_name() :: atom(). %% parent = Message or AVP
+-type parent_record() :: tuple() | avp_values() | map().
+-type avp_name() :: atom().
+-type avp_record() :: tuple().
+-type avp_values() :: [{avp_name(), term()}].
+
+-type non_grouped_avp() :: #diameter_avp{}.
+-type grouped_avp() :: nonempty_improper_list(#diameter_avp{}, [avp()]).
+-type avp() :: non_grouped_avp() | grouped_avp().
+
+%% The arbitrary arity returned from dictionary avp_arity functions.
+-define(ANY, {0, '*'}).
+
+%% ---------------------------------------------------------------------------
+%% # encode_avps/3
+%% ---------------------------------------------------------------------------
+
+-spec encode_avps(parent_name(), parent_record(), map())
+ -> iolist()
+ | no_return().
+
+encode_avps(Name, Vals, #{module := Mod} = Opts) ->
+ Strict = mget(strict_arities, Opts, encode),
+ try
+ encode(Name, Vals, Opts, Strict, Mod)
+ catch
+ throw: {?MODULE, Reason} ->
+ diameter_lib:log({encode, error},
+ ?MODULE,
+ ?LINE,
+ {Reason, Name, Vals, Mod}),
+ erlang:error(list_to_tuple(Reason ++ [Name]));
+ error: Reason: Stack ->
+ diameter_lib:log({encode, failure},
+ ?MODULE,
+ ?LINE,
+ {Reason, Name, Vals, Mod, Stack}),
+ erlang:error({encode_failure, Reason, Name, Stack})
+ end.
+
+%% encode/5
+
+encode(Name, Vals, Opts, Strict, Mod)
+ when is_list(Vals) ->
+ case Opts of
+ #{ordered_encode := false} ->
+ lists:map(fun({F,V}) -> encode(Name, F, V, Opts, Strict, Mod) end,
+ Vals);
+ _ ->
+ Rec = Mod:'#set-'(Vals, newrec(Mod, Name)),
+ encode(Name, Rec, Opts, Strict, Mod)
+ end;
+
+encode(Name, Map, Opts, Strict, Mod)
+ when is_map(Map) ->
+ [enc(F, A, V, Opts, Strict, Mod) || {F,A} <- Mod:avp_arity(Name),
+ V <- [mget(F, Map, undefined)]];
+
+encode(Name, Rec, Opts, Strict, Mod) ->
+ [encode(Name, F, V, Opts, Strict, Mod) || {F,V} <- Mod:'#get-'(Rec)].
+
+%% encode/6
+
+encode(_, AvpName, Values, Opts, Strict, Mod)
+ when Strict /= encode ->
+ enc(AvpName, ?ANY, Values, Opts, Strict, Mod);
+
+encode(Name, AvpName, Values, Opts, Strict, Mod) ->
+ Arity = Mod:avp_arity(Name, AvpName),
+ enc(AvpName, Arity, Values, Opts, Strict, Mod).
+
+%% enc/6
+
+enc(AvpName, Arity, Values, Opts, Strict, Mod)
+ when Strict /= encode, Arity /= ?ANY ->
+ enc(AvpName, ?ANY, Values, Opts, Strict, Mod);
+
+enc(AvpName, 1, undefined, _, _, _) ->
+ ?THROW([mandatory_avp_missing, AvpName]);
+
+enc(AvpName, 1, Value, Opts, _, Mod) ->
+ H = avp_header(AvpName, Mod),
+ enc(AvpName, H, Value, Opts, Mod);
+
+enc(_, {0,_}, [], _, _, _) ->
+ [];
+
+enc(_, _, undefined, _, _, _) ->
+ [];
+
+%% Be forgiving when a list of values is expected. If the value itself
+%% is a list then the user has to wrap it to avoid each member from
+%% being interpreted as an individual AVP value.
+enc(AvpName, Arity, V, Opts, Strict, Mod)
+ when not is_list(V) ->
+ enc(AvpName, Arity, [V], Opts, Strict, Mod);
+
+enc(AvpName, {Min, Max}, Values, Opts, Strict, Mod) ->
+ H = avp_header(AvpName, Mod),
+ enc(AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod).
+
+%% enc/9
+
+enc(AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod)
+ when Strict /= encode;
+ Max == '*', Min =< N ->
+ [enc(AvpName, H, V, Opts, Mod) || V <- Vs];
+
+enc(AvpName, _, Min, N, _, [], _, _, _)
+ when N < Min ->
+ ?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]);
+
+enc(_, _, _, _, _, [], _, _, _) ->
+ [];
+
+enc(AvpName, _, _, N, Max, _, _, _, _)
+ when Max =< N ->
+ ?THROW([repeated_avp_excessive_arity, AvpName, Max]);
+
+enc(AvpName, H, Min, N, Max, [V|Vs], Opts, Strict, Mod) ->
+ [enc(AvpName, H, V, Opts, Mod)
+ | enc(AvpName, H, Min, N+1, Max, Vs, Opts, Strict, Mod)].
+
+%% avp_header/2
+
+avp_header('AVP', _) ->
+ false;
+
+avp_header(AvpName, Mod) ->
+ {_,_,_} = Mod:avp_header(AvpName).
+
+%% enc/5
+
+enc('AVP', false, Value, Opts, Mod) ->
+ enc_AVP(Value, Opts, Mod);
+
+enc(AvpName, Hdr, Value, Opts, Mod) ->
+ enc1(AvpName, Hdr, Value, Opts, Mod).
+
+%% enc1/5
+
+enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod) ->
+ diameter_codec:pack_data(Hdr, Mod:avp(encode, Value, AvpName, Opts)).
+
+%% enc1/6
+
+enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod, Dict) ->
+ diameter_codec:pack_data(Hdr, avp(encode, Value, AvpName, Opts, Mod, Dict)).
+
+%% enc_AVP/3
+
+%% No value: assume AVP data is already encoded. The normal case will
+%% be when this is passed back from #diameter_packet.errors as a
+%% consequence of a failed decode. Any AVP can be encoded this way
+%% however, which side-steps any arity checks for known AVP's and
+%% could potentially encode something unfortunate.
+enc_AVP(#diameter_avp{value = undefined} = A, Opts, _) ->
+ diameter_codec:pack_avp(A, Opts);
+
+%% Encode a name/value pair using an alternate dictionary if need be ...
+enc_AVP(#diameter_avp{name = AvpName, value = Value}, Opts, Mod) ->
+ enc_AVP(AvpName, Value, Opts, Mod);
+enc_AVP({AvpName, Value}, Opts, Mod) ->
+ enc_AVP(AvpName, Value, Opts, Mod);
+
+%% ... or with a specified dictionary.
+enc_AVP({Dict, AvpName, Value}, Opts, Mod) ->
+ enc1(AvpName, Dict:avp_header(AvpName), Value, Opts, Mod, Dict).
+
+%% Don't guard against anything being sent as a generic 'AVP', which
+%% allows arity restrictions to be abused.
+
+%% enc_AVP/4
+
+enc_AVP(AvpName, Value, Opts, Mod) ->
+ try Mod:avp_header(AvpName) of
+ H ->
+ enc1(AvpName, H, Value, Opts, Mod)
+ catch
+ error: _ ->
+ Dicts = mget(avp_dictionaries, Opts, []),
+ enc_AVP(Dicts, AvpName, Value, Opts, Mod)
+ end.
+
+%% enc_AVP/5
+
+enc_AVP([Dict | Rest], AvpName, Value, Opts, Mod) ->
+ try Dict:avp_header(AvpName) of
+ H ->
+ enc1(AvpName, H, Value, Opts, Mod, Dict)
+ catch
+ error: _ ->
+ enc_AVP(Rest, AvpName, Value, Opts, Mod)
+ end;
+
+enc_AVP([], AvpName, _, _, _) ->
+ ?THROW([no_dictionary, AvpName]).
+
+%% ---------------------------------------------------------------------------
+%% # decode_avps/3
+%% ---------------------------------------------------------------------------
+
+-spec decode_avps(parent_name(), binary(), map())
+ -> {parent_record() | parent_name(), [avp()], Failed}
+ when Failed :: [{5000..5999, #diameter_avp{}}].
+
+decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) ->
+ Strict = mget(strict_arities, Opts, decode),
+ [AM, Avps, Failed | Rec]
+ = decode(Bin, Name, Mod, Fmt, Strict, Opts, 0, #{}),
+ %% AM counts the number of top-level AVPs, which missing/5 then
+ %% uses when appending 5005 errors.
+ {reformat(Name, Rec, Strict, Mod, Fmt),
+ Avps,
+ Failed ++ missing(Name, Strict, Mod, Opts, AM)}.
+
+%% Append arity errors so that errors are reported in the order
+%% encountered. Failed-AVP should typically contain the first
+%% error encountered.
+
+%% decode/8
+
+decode(<<Code:32, V:1, M:1, P:1, _:5, Len:24, I:V/unit:32, Rest/binary>>,
+ Name,
+ Mod,
+ Fmt,
+ Strict,
+ Opts,
+ Idx,
+ AM) ->
+ decode(Rest,
+ Code,
+ if 1 == V -> I; true -> undefined end,
+ Len - 8 - 4*V, %% possibly negative, causing case match to fail
+ (4 - (Len rem 4)) rem 4,
+ 1 == M,
+ 1 == P,
+ Name,
+ Mod,
+ Fmt,
+ Strict,
+ Opts,
+ Idx,
+ AM);
+
+decode(<<>>, Name, Mod, Fmt, Strict, _, _, AM) ->
+ [AM, [], [] | newrec(Fmt, Mod, Name, Strict)];
+
+decode(Bin, Name, Mod, Fmt, Strict, _, Idx, AM) ->
+ Avp = #diameter_avp{data = Bin, index = Idx},
+ [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)].
+
+%% decode/14
+
+decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0,
+ Idx, AM0) ->
+ case Bin of
+ <<Data:DataLen/binary, _:Pad/binary, T/binary>> ->
+ {NameT, Field, Arity, {I, AM}}
+ = incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0),
+
+ Opts = setopts(NameT, Name, M, Opts0),
+ %% Not AvpName or else a failed Failed-AVP
+ %% decode is packed into 'AVP'.
+
+ Avp = #diameter_avp{code = Code,
+ vendor_id = Vid,
+ is_mandatory = M,
+ need_encryption = P,
+ data = Data,
+ name = name(NameT),
+ type = type(NameT),
+ index = Idx},
+
+ Dec = dec(Data, Name, NameT, Mod, Fmt, Opts, Avp),
+ Acc = decode(T, Name, Mod, Fmt, Strict, Opts0, Idx+1, AM),%% recurse
+ acc(Acc, Dec, I, Field, Arity, Strict, Mod);
+ _ ->
+ {NameT, _Field, _Arity, {_, AM}}
+ = incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0),
+
+ Avp = #diameter_avp{code = Code,
+ vendor_id = Vid,
+ is_mandatory = M,
+ need_encryption = P,
+ data = Bin,
+ name = name(NameT),
+ type = type(NameT),
+ index = Idx},
+
+ [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)]
+ end.
+
+%% incr/8
+
+incr(Name, Code, Vid, M, Mod, Strict, Opts, AM0) ->
+ NameT = Mod:avp_name(Code, Vid), %% {AvpName, Type} | 'AVP'
+ Field = field(NameT), %% AvpName | 'AVP'
+ Arity = avp_arity(Name, Field, Mod, Opts, M),
+ if 0 == Arity, 'AVP' /= Field ->
+ A = pack_arity(Name, Field, Opts, Mod, M),
+ {NameT, 'AVP', A, incr('AVP', A, Strict, AM0)};
+ true ->
+ {NameT, Field, Arity, incr(Field, Arity, Strict, AM0)}
+ end.
+
+%% Data is a truncated header if command_code = undefined, otherwise
+%% payload bytes. The former is padded to the length of a header if
+%% the AVP reaches an outgoing encode.
+%%
+%% RFC 6733 says that an AVP returned with 5014 can contain a minimal
+%% payload for the AVP's type, but don't always know the type.
+
+setopts('AVP', _, _, Opts) ->
+ Opts;
+
+setopts({_, Type}, Name, M, Opts) ->
+ set_failed(Name, set_strict(Type, M, Opts)).
+
+%% incr/4
+
+incr(_, A, SA, AM)
+ when A == ?ANY;
+ A == 0;
+ SA /= decode ->
+ {undefined, AM};
+
+incr(AvpName, _, _, AM) ->
+ case AM of
+ #{AvpName := N} ->
+ {N, AM#{AvpName => N+1}};
+ _ ->
+ {0, AM#{AvpName => 1}}
+ end.
+
+%% mget/3
+%%
+%% Measurably faster than maps:get/3.
+
+mget(Key, Map, Def) ->
+ case Map of
+ #{Key := V} ->
+ V;
+ _ ->
+ Def
+ end.
+
+%% name/1
+
+name({Name, _}) ->
+ Name;
+name(_) ->
+ undefined.
+
+%% type/1
+
+type({_, Type}) ->
+ Type;
+type(_) ->
+ undefined.
+
+%% missing/5
+
+missing(Name, decode, Mod, Opts, AM) ->
+ [{5005, empty_avp(N, Opts, Mod)} || {N,A} <- Mod:avp_arity(Name),
+ N /= 'AVP',
+ Mn <- [min_arity(A)],
+ 0 < Mn,
+ mget(N, AM, 0) < Mn];
+
+missing(_, _, _, _, _) ->
+ [].
+
+%% 3588/6733:
+%%
+%% DIAMETER_MISSING_AVP 5005
+%% The request did not contain an AVP that is required by the Command
+%% Code definition. If this value is sent in the Result-Code AVP, a
+%% Failed-AVP AVP SHOULD be included in the message. The Failed-AVP
+%% AVP MUST contain an example of the missing AVP complete with the
+%% Vendor-Id if applicable. The value field of the missing AVP
+%% should be of correct minimum length and contain zeros.
+
+%% min_arity/1
+
+min_arity(1) ->
+ 1;
+min_arity({Mn,_}) ->
+ Mn.
+
+%% empty_avp/3
+
+empty_avp('AVP', _, _) ->
+ #diameter_avp{data = <<0:64>>};
+
+empty_avp(Name, Opts, Mod) ->
+ {Code, Flags, VId} = Mod:avp_header(Name),
+ {Name, Type} = Mod:avp_name(Code, VId),
+ #diameter_avp{name = Name,
+ code = Code,
+ vendor_id = VId,
+ is_mandatory = 0 /= (Flags band 2#01000000),
+ need_encryption = 0 /= (Flags band 2#00100000),
+ data = Mod:empty_value(Name, Opts),
+ type = Type}.
+
+%% 3588, ch 7:
+%%
+%% The Result-Code AVP describes the error that the Diameter node
+%% encountered in its processing. In case there are multiple errors,
+%% the Diameter node MUST report only the first error it encountered
+%% (detected possibly in some implementation dependent order). The
+%% specific errors that can be described by this AVP are described in
+%% the following section.
+
+%% field/1
+
+field({AvpName, _}) ->
+ AvpName;
+field(_) ->
+ 'AVP'.
+
+%% dec/7
+
+%% AVP not in dictionary: try an alternate.
+
+dec(Data, Name, 'AVP', Mod, Fmt, Opts, Avp) ->
+ dec_AVP(dicts(Mod, Opts), Data, Name, Mod, Fmt, Opts, Avp);
+
+%% 6733, 4.4:
+%%
+%% Receivers of a Grouped AVP that does not have the 'M' (mandatory)
+%% bit set and one or more of the encapsulated AVPs within the group
+%% has the 'M' (mandatory) bit set MAY simply be ignored if the
+%% Grouped AVP itself is unrecognized. The rule applies even if the
+%% encapsulated AVP with its 'M' (mandatory) bit set is further
+%% encapsulated within other sub-groups, i.e., other Grouped AVPs
+%% embedded within the Grouped AVP.
+%%
+%% The first sentence is slightly mangled, but take it to mean this:
+%%
+%% An unrecognized AVP of type Grouped that does not set the 'M' bit
+%% MAY be ignored even if one of its encapsulated AVPs sets the 'M'
+%% bit.
+%%
+%% The text above is a change from RFC 3588, which instead says this:
+%%
+%% Further, if any of the AVPs encapsulated within a Grouped AVP has
+%% the 'M' (mandatory) bit set, the Grouped AVP itself MUST also
+%% include the 'M' bit set.
+%%
+%% Both of these texts have problems. If the AVP is unknown then its
+%% type is unknown since the type isn't sent over the wire, so the
+%% 6733 text becomes a non-statement: don't know that the AVP not
+%% setting the M-bit is of type Grouped, therefore can't know that its
+%% data consists of encapsulated AVPs, therefore can't but ignore that
+%% one of these might set the M-bit. It should be no worse if we know
+%% the AVP to have type Grouped.
+%%
+%% Similarly, for the 3588 text: if we receive an AVP that doesn't set
+%% the M-bit and don't know that the AVP has type Grouped then we
+%% can't realize that its data contains an AVP that sets the M-bit, so
+%% can't regard the AVP as erroneous on this account. Again, it should
+%% be no worse if the type is known to be Grouped, but in this case
+%% the RFC forces us to regard the AVP as erroneous. This is
+%% inconsistent, and the 3588 text has never been enforced.
+%%
+%% So, if an AVP doesn't set the M-bit then we're free to ignore it,
+%% regardless of the AVP's type. If we know the type to be Grouped
+%% then we must ignore the M-bit on an encapsulated AVP. That means
+%% packing such an encapsulated AVP into an 'AVP' field if need be,
+%% not regarding the lack of a specific field as an error as is
+%% otherwise the case. (The lack of an AVP-specific field being how we
+%% defined the RFC's "unrecognized", which is slightly stronger than
+%% "not defined".)
+
+dec(Data, Name, {AvpName, Type}, Mod, Fmt, Opts, Avp) ->
+ #{app_dictionary := AppMod, failed_avp := Failed}
+ = Opts,
+
+ %% Reset the dictionary for best-effort decode of Failed-AVP.
+ Dict = if Failed -> AppMod;
+ true -> Mod
+ end,
+
+ dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, Avp).
+
+%% dicts/2
+
+dicts(Mod, #{app_dictionary := Mod, avp_dictionaries := Dicts}) ->
+ Dicts;
+
+dicts(_, #{app_dictionary := Dict, avp_dictionaries := Dicts}) ->
+ [Dict | Dicts];
+
+dicts(Mod, #{app_dictionary := Mod}) ->
+ [];
+
+dicts(_, #{app_dictionary := Dict}) ->
+ [Dict].
+
+%% dec/10
+
+dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, Avp) ->
+ try avp(decode, Data, AvpName, Opts, Mod, Dict) of
+ V ->
+ set(Type, Fmt, Avp, V)
+ catch
+ throw: {?MODULE, T} ->
+ decode_error(Failed, Fmt, T, Avp);
+ error: Reason: Stack ->
+ decode_error(Failed, Reason, Stack, Name, Mod, Opts, Avp)
+ end.
+
+%% dec_AVP/7
+
+dec_AVP([], _, _, _, _, _, Avp) ->
+ Avp;
+
+dec_AVP(Dicts, Data, Name, Mod, Fmt, Opts, #diameter_avp{code = Code,
+ vendor_id = Vid}
+ = Avp) ->
+ dec_AVP(Dicts, Data, Name, Mod, Fmt, Opts, Code, Vid, Avp).
+
+%% dec_AVP/9
+%%
+%% Try to decode an AVP in the first alternate dictionary that defines
+%% it.
+
+dec_AVP([Dict | Rest], Data, Name, Mod, Fmt, Opts0, Code, Vid, Avp) ->
+ case Dict:avp_name(Code, Vid) of
+ {AvpName, Type} = NameT ->
+ A = Avp#diameter_avp{name = AvpName,
+ type = Type},
+ #{failed_avp := Failed}
+ = Opts
+ = setopts(NameT, Name, Avp#diameter_avp.is_mandatory, Opts0),
+ dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, A);
+ _ ->
+ dec_AVP(Rest, Data, Name, Mod, Fmt, Opts0, Code, Vid, Avp)
+ end;
+
+dec_AVP([], _, _, _, _, _, _, _, Avp) ->
+ Avp.
+
+%% set/4
+%%
+%% A Grouped AVP is represented as a #diameter_avp{} list with AVP
+%% as head and component AVPs as tail.
+
+set('Grouped', Fmt, Avp, V) ->
+ {Rec, As} = V,
+ [set(Fmt, Avp, Rec) | As];
+
+set(_, _, Avp, V) ->
+ Avp#diameter_avp{value = V}.
+
+%% decode_error/4
+%%
+%% Error when decoding a grouped AVP.
+
+%% Ignoring errors in Failed-AVP.
+decode_error(true, Fmt, {Rec, ComponentAvps, _Errors}, Avp) ->
+ [set(Fmt, Avp, Rec) | ComponentAvps];
+
+%% Or not. A faulty component is encoded by itself in Failed-AVP, as
+%% suggested by 7.5 of RFC 6733 (quoted below), so that errors are
+%% reported unambigiously.
+decode_error(false, _, {_, ComponentAvps, [{RC,A} | _]}, Avp) ->
+ {RC, [Avp | ComponentAvps], Avp#diameter_avp{data = [A]}}.
+
+%% set/3
+
+set(none, Avp, _Name) ->
+ Avp;
+set(_, Avp, Rec) ->
+ Avp#diameter_avp{value = Rec}.
+
+%% decode_error/7
+%%
+%% Error when decoding a non-grouped AVP.
+
+decode_error(true, _, _, _, _, _, Avp) ->
+ Avp;
+
+decode_error(false, Reason, Stack, Name, Mod, Opts, Avp) ->
+ Z = diameter_lib:stacktrace(Stack),
+ diameter_lib:log(decode_error,
+ ?MODULE,
+ ?LINE,
+ {Reason, Name, Avp#diameter_avp.name, Mod, Z}),
+ case Reason of
+ {'DIAMETER', 5014 = RC, _} ->
+ %% Length error communicated from diameter_types or a
+ %% @custom_types/@codecs module.
+ AvpName = Avp#diameter_avp.name,
+ {RC, Avp#diameter_avp{data = Mod:empty_value(AvpName, Opts)}};
+ _ ->
+ {5004, Avp}
+ end.
+
+%% 3588/6733:
+%%
+%% DIAMETER_INVALID_AVP_VALUE 5004
+%% The request contained an AVP with an invalid value in its data
+%% portion. A Diameter message indicating this error MUST include
+%% the offending AVPs within a Failed-AVP AVP.
+
+%% avp/6
+
+avp(T, Data, AvpName, Opts, Mod, Mod) ->
+ Mod:avp(T, Data, AvpName, Opts);
+
+avp(T, Data, AvpName, Opts, _, Mod) ->
+ Mod:avp(T, Data, AvpName, Opts#{module := Mod}).
+
+%% set_strict/3
+%%
+%% Set false as soon as we see a Grouped AVP that doesn't set the
+%% M-bit, to ignore the M-bit on an encapsulated AVP.
+
+set_strict('Grouped', false = M, #{strict_mbit := true} = Opts) ->
+ Opts#{strict_mbit := M};
+set_strict(_, _, Opts) ->
+ Opts.
+
+%% set_failed/2
+%%
+%% Set true as soon as we see Failed-AVP. Matching on 'Failed-AVP'
+%% assumes that this is the RFC AVP. Strictly, this doesn't need to be
+%% the case.
+
+set_failed('Failed-AVP', #{failed_avp := false} = Opts) ->
+ Opts#{failed_avp := true};
+set_failed(_, Opts) ->
+ Opts.
+
+%% acc/7
+
+acc([AM | Acc], As, I, Field, Arity, Strict, Mod) ->
+ [AM | acc1(Acc, As, I, Field, Arity, Strict, Mod)].
+
+%% acc1/7
+
+%% Faulty AVP, not grouped.
+acc1(Acc, {_RC, Avp} = E, _, _, _, _, _) ->
+ [Avps, Failed | Rec] = Acc,
+ [[Avp | Avps], [E | Failed] | Rec];
+
+%% Faulty component in grouped AVP.
+acc1(Acc, {RC, As, Avp}, _, _, _, _, _) ->
+ [Avps, Failed | Rec] = Acc,
+ [[As | Avps], [{RC, Avp} | Failed] | Rec];
+
+%% Grouped AVP ...
+acc1([Avps | Acc], [Avp|_] = As, I, Field, Arity, Strict, Mod) ->
+ [[As|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod)];
+
+%% ... or not.
+acc1([Avps | Acc], Avp, I, Field, Arity, Strict, Mod) ->
+ [[Avp|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod)].
+
+%% The component list of a Grouped AVP is discarded when packing into
+%% the record (or equivalent): the values in an 'AVP' field are
+%% diameter_avp records, not a list of records in the Grouped case,
+%% and the decode into the value field is best-effort. The reason is
+%% history more than logic: it would probably have made more sense to
+%% retain the same structure as in diameter_packet.avps, but an 'AVP'
+%% list has always been flat.
+
+%% acc2/7
+
+%% No errors, but nowhere to pack.
+acc2(Acc, Avp, _, 'AVP', 0, _, _) ->
+ [Failed | Rec] = Acc,
+ [[{rc(Avp), Avp} | Failed] | Rec];
+
+%% Relaxed arities.
+acc2(Acc, Avp, _, Field, Arity, Strict, Mod)
+ when Strict /= decode ->
+ pack(Arity, Field, Avp, Mod, Acc);
+
+%% No maximum arity.
+acc2(Acc, Avp, _, Field, {_,'*'} = Arity, _, Mod) ->
+ pack(Arity, Field, Avp, Mod, Acc);
+
+%% Or check.
+acc2(Acc, Avp, I, Field, Arity, _, Mod) ->
+ Mx = max_arity(Arity),
+ if Mx =< I ->
+ [Failed | Rec] = Acc,
+ [[{5009, Avp} | Failed] | Rec];
+ true ->
+ pack(Arity, Field, Avp, Mod, Acc)
+ end.
+
+%% 3588/6733:
+%%
+%% DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009
+%% A message was received that included an AVP that appeared more
+%% often than permitted in the message definition. The Failed-AVP
+%% AVP MUST be included and contain a copy of the first instance of
+%% the offending AVP that exceeded the maximum number of occurrences
+
+%% max_arity/1
+
+max_arity(1) ->
+ 1;
+max_arity({_,Mx}) ->
+ Mx.
+
+%% rc/1
+
+rc(#diameter_avp{is_mandatory = M}) ->
+ if M -> 5001; true -> 5008 end.
+
+%% 3588:
+%%
+%% DIAMETER_AVP_UNSUPPORTED 5001
+%% The peer received a message that contained an AVP that is not
+%% recognized or supported and was marked with the Mandatory bit. A
+%% Diameter message with this error MUST contain one or more Failed-
+%% AVP AVP containing the AVPs that caused the failure.
+%%
+%% DIAMETER_AVP_NOT_ALLOWED 5008
+%% A message was received with an AVP that MUST NOT be present. The
+%% Failed-AVP AVP MUST be included and contain a copy of the
+%% offending AVP.
+
+%% pack_arity/5
+
+%% Give Failed-AVP special treatment since (1) it'll contain any
+%% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to
+%% allow for Failed-AVP in an answer-message.
+
+pack_arity(Name, AvpName, _, Mod, M)
+ when Name == 'Failed-AVP';
+ Name == 'answer-message', AvpName == 'Failed-AVP';
+ not M ->
+ Mod:avp_arity(Name, 'AVP');
+%% Not testing just Name /= 'Failed-AVP' means we're changing the
+%% packing of AVPs nested within Failed-AVP, but the point of
+%% ignoring errors within Failed-AVP is to decode as much as
+%% possible, and failing because a mandatory AVP couldn't be
+%% packed into a dedicated field defeats that point.
+
+pack_arity(Name, _, #{strict_mbit := Strict, failed_avp := Failed}, Mod, _)
+ when not Strict;
+ Failed ->
+ Mod:avp_arity(Name, 'AVP');
+
+pack_arity(_, _, _, _, _) ->
+ 0.
+
+%% avp_arity/5
+
+avp_arity(Name, 'AVP' = AvpName, Mod, Opts, M) ->
+ pack_arity(Name, AvpName, Opts, Mod, M);
+
+avp_arity(Name, AvpName, Mod, _, _) ->
+ Mod:avp_arity(Name, AvpName).
+
+%% pack/5
+
+pack(Arity, F, Avp, Mod, [Failed | Rec]) ->
+ [Failed | set(Arity, F, value(F, Avp), Mod, Rec)].
+
+%% set/5
+
+set(_, _, _, _, Name)
+ when is_atom(Name) ->
+ Name;
+
+set(1, F, Value, _, Map)
+ when is_map(Map) ->
+ Map#{F => Value};
+
+set(_, F, V, _, Map)
+ when is_map(Map) ->
+ maps:update_with(F, fun(Vs) -> [V|Vs] end, [V], Map);
+
+set(1, F, Value, Mod, Rec) ->
+ Mod:'#set-'({F, Value}, Rec);
+
+set(_, F, V, Mod, Rec) ->
+ Vs = Mod:'#get-'(F, Rec),
+ Mod:'#set-'({F, [V|Vs]}, Rec).
+
+%% value/2
+
+value('AVP', Avp) ->
+ Avp;
+
+value(_, #diameter_avp{value = V}) ->
+ V.
+
+%% ---------------------------------------------------------------------------
+%% # grouped_avp/3
+%% ---------------------------------------------------------------------------
+
+%% Note that Grouped is the only AVP type that doesn't just return a
+%% decoded value, also returning the list of component diameter_avp
+%% records.
+
+-spec grouped_avp(decode, avp_name(), binary(), term())
+ -> {avp_record(), [avp()]};
+ (encode, avp_name(), avp_record() | avp_values(), term())
+ -> iolist()
+ | no_return().
+
+%% An error in decoding a component AVP throws the first faulty
+%% component, which a catch wraps in the Grouped AVP in question. A
+%% partially decoded record is only used when ignoring errors in
+%% Failed-AVP.
+grouped_avp(decode, Name, Bin, Opts) ->
+ {Rec, Avps, Es} = T = decode_avps(Name, Bin, Opts),
+ [] == Es orelse ?THROW(T),
+ {Rec, Avps};
+
+grouped_avp(encode, Name, Data, Opts) ->
+ encode_avps(Name, Data, Opts).
+
+%% 7.5. Failed-AVP AVP
+
+%% In the case where the offending AVP is embedded within a Grouped AVP,
+%% the Failed-AVP MAY contain the grouped AVP, which in turn contains
+%% the single offending AVP. The same method MAY be employed if the
+%% grouped AVP itself is embedded in yet another grouped AVP and so on.
+%% In this case, the Failed-AVP MAY contain the grouped AVP hierarchy up
+%% to the single offending AVP. This enables the recipient to detect
+%% the location of the offending AVP when embedded in a group.
+
+%% ---------------------------------------------------------------------------
+%% # empty_group/2
+%% ---------------------------------------------------------------------------
+
+empty_group(Name, #{module := Mod} = Opts) ->
+ list_to_binary([z(F, A, Opts, Mod) || {F,A} <- Mod:avp_arity(Name)]).
+
+z(Name, 1, Opts, Mod) ->
+ z(Name, Opts, Mod);
+z(_, {0,_}, _, _) ->
+ [];
+z(Name, {Min, _}, Opts, Mod) ->
+ binary:copy(z(Name, Opts, Mod), Min).
+
+z('AVP', _, _) ->
+ <<0:64>>; %% minimal header
+z(Name, Opts, Mod) ->
+ Bin = diameter_codec:pack_data(Mod:avp_header(Name),
+ Mod:empty_value(Name, Opts)),
+ Sz = iolist_size(Bin),
+ <<0:Sz/unit:8>>.
+
+%% ---------------------------------------------------------------------------
+%% # empty/2
+%% ---------------------------------------------------------------------------
+
+empty(Name, #{module := Mod} = Opts) ->
+ Mod:avp(encode, zero, Name, Opts).
+
+%% ------------------------------------------------------------------------------
+
+%% newrec/4
+
+newrec(none, _, Name, _) ->
+ Name;
+
+newrec(record, Mod, Name, T)
+ when T /= decode ->
+ RecName = Mod:name2rec(Name),
+ Sz = Mod:'#info-'(RecName, size),
+ erlang:make_tuple(Sz, [], [{1, RecName}]);
+
+newrec(record, Mod, Name, _) ->
+ newrec(Mod, Name);
+
+newrec(_, _, _, _) ->
+ #{}.
+
+%% newrec/2
+
+newrec(Mod, Name) ->
+ Mod:'#new-'(Mod:name2rec(Name)).
+
+%% reformat/5
+
+reformat(Name, Map, _Strict, Mod, list) ->
+ [{F,V} || {F,_} <- Mod:avp_arity(Name), #{F := V} <- [Map]];
+
+reformat(Name, Map, Strict, Mod, record_from_map) ->
+ RecName = Mod:name2rec(Name),
+ list_to_tuple([RecName | [mget(F, Map, def(A, Strict))
+ || {F,A} <- Mod:avp_arity(Name)]]);
+
+reformat(_, Rec, _, _, _) ->
+ Rec.
+
+%% def/2
+
+def(1, decode) ->
+ undefined;
+
+def(_, _) ->
+ [].
diff --git a/lib/diameter/src/base/diameter_internal.hrl b/lib/diameter/src/base/diameter_internal.hrl
index a0f4a8567d..4a678643c2 100644
--- a/lib/diameter/src/base/diameter_internal.hrl
+++ b/lib/diameter/src/base/diameter_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -35,9 +35,6 @@
%% A corresponding error when failure is the best option.
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
-%% Failure reports always get a stack trace.
--define(STACK, erlang:get_stacktrace()).
-
%% Warning report for unexpected messages in various processes.
-define(UNEXPECTED(F,A),
diameter_lib:warning_report(unexpected, {?MODULE, F, A})).
diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl
index 3928769b5e..edd9d5a4ce 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-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -33,26 +33,22 @@
time/1,
eval/1,
eval_name/1,
- get_stacktrace/0,
+ stacktrace/1,
ipaddr/1,
spawn_opts/2,
wait/1,
- fold_tuple/3,
fold_n/3,
for_n/2,
log/4]).
%% ---------------------------------------------------------------------------
-%% # get_stacktrace/0
+%% # stacktrace/1
%% ---------------------------------------------------------------------------
%% Return a stacktrace with a leading, potentially large, argument
-%% list replaced by an arity. Trace on stacktrace/0 to see the
+%% list replaced by an arity. Trace on stacktrace/1 to see the
%% original.
-get_stacktrace() ->
- stacktrace(erlang:get_stacktrace()).
-
stacktrace([{M,F,A,L} | T]) when is_list(A) ->
[{M, F, length(A), L} | T];
stacktrace(L) ->
@@ -269,8 +265,8 @@ ipaddr(Addr) ->
try
ip(Addr)
catch
- error: _ ->
- erlang:error({invalid_address, erlang:get_stacktrace()})
+ error: _: Stack ->
+ erlang:error({invalid_address, Stack})
end.
%% Already a tuple: ensure non-negative integers of the right size.
@@ -284,7 +280,7 @@ ip(T)
%% Or not: convert from '.'/':'-separated decimal/hex.
ip(Addr) ->
- {ok, A} = inet_parse:address(Addr), %% documented in inet(3)
+ {ok, A} = inet:parse_address(Addr),
A.
%% ---------------------------------------------------------------------------
@@ -341,36 +337,6 @@ down(MRef)
receive {'DOWN', MRef, process, _, _} = T -> T end.
%% ---------------------------------------------------------------------------
-%% # 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'.
-%%
-%% eg. fold_tuple(2, Hdr, #diameter_header{end_to_end_id = 42})
-
-fold_tuple(_, T, undefined) ->
- T;
-
-fold_tuple(N, T0, T1) ->
- {_, T} = lists:foldl(fun(V, {I,_} = IT) -> {I+1, ft(V, IT)} end,
- {N, T0},
- lists:nthtail(N-1, tuple_to_list(T1))),
- T.
-
-ft(undefined, {_, T}) ->
- T;
-ft(Value, {Idx, T}) ->
- setelement(Idx, T, Value).
-
-%% ---------------------------------------------------------------------------
%% # fold_n/3
%% ---------------------------------------------------------------------------
diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl
index 2759f17e64..4cb5a57a54 100644
--- a/lib/diameter/src/base/diameter_peer.erl
+++ b/lib/diameter/src/base/diameter_peer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -202,10 +202,10 @@ match1(Addr, Match) ->
match(Addr, {ok, A}, _) ->
Addr == A;
match(Addr, {error, _}, RE) ->
- match == re:run(inet_parse:ntoa(Addr), RE, [{capture, none}]).
+ match == re:run(inet:ntoa(Addr), RE, [{capture, none}, caseless]).
addr([_|_] = A) ->
- inet_parse:address(A);
+ inet:parse_address(A);
addr(A) ->
{ok, A}.
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 7ee1e5fe59..cf5e7f21d3 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -128,7 +128,13 @@
%% outgoing DPR; boolean says whether or not
%% the request was sent explicitly with
%% diameter:call/4.
+ codec :: #{decode_format := diameter:decode_format(),
+ string_decode := boolean(),
+ strict_mbit := boolean(),
+ rfc := 3588 | 6733,
+ ordered_encode := false},
strict :: boolean(),
+ ack = false :: boolean(),
length_errors :: exit | handle | discard,
incoming_maxlen :: integer() | infinity}).
@@ -159,10 +165,7 @@
%% # start/3
%% ---------------------------------------------------------------------------
--spec start(T, [Opt], {[diameter:service_opt()],
- [node()],
- module(),
- #diameter_service{}})
+-spec start(T, [Opt], {map(), [node()], module(), #diameter_service{}})
-> {reference(), pid()}
when T :: {connect|accept, diameter:transport_ref()},
Opt :: diameter:transport_opt().
@@ -221,9 +224,10 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
erlang:monitor(process, WPid),
wait(Ack, WPid),
diameter_stats:reg(Ref),
- diameter_codec:setopts([{common_dictionary, Dict0} | SvcOpts]),
- {_,_} = Mask = proplists:get_value(sequence, SvcOpts),
- Maxlen = proplists:get_value(incoming_maxlen, SvcOpts, 16#FFFFFF),
+
+ #{sequence := Mask, incoming_maxlen := Maxlen}
+ = SvcOpts,
+
{[Cs,Ds], Rest} = proplists:split(Opts, [capabilities_cb, disconnect_cb]),
putr(?CB_KEY, {Ref, [F || {_,F} <- Cs]}),
putr(?DPR_KEY, [F || {_, F} <- Ds]),
@@ -234,8 +238,8 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
proplists:get_value(dpa_timeout, Opts, ?DPA_TIMEOUT)}),
Tmo = proplists:get_value(capx_timeout, Opts, ?CAPX_TIMEOUT),
- Strictness = proplists:get_value(capx_strictness, Opts, true),
- OnLengthErr = proplists:get_value(length_errors, Opts, exit),
+ Strict = proplists:get_value(strict_capx, Opts, true),
+ LengthErr = proplists:get_value(length_errors, Opts, exit),
{TPid, Addrs} = start_transport(T, Rest, Svc),
@@ -247,9 +251,15 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
dictionary = Dict0,
mode = M,
service = svc(Svc, Addrs),
- length_errors = OnLengthErr,
- strict = Strictness,
- incoming_maxlen = Maxlen}.
+ length_errors = LengthErr,
+ strict = Strict,
+ incoming_maxlen = Maxlen,
+ codec = maps:with([decode_format,
+ string_decode,
+ strict_mbit,
+ rfc,
+ ordered_encode],
+ SvcOpts#{ordered_encode => false})}.
%% The transport returns its local ip addresses so that different
%% transports on the same service can use different local addresses.
%% The local addresses are put into Host-IP-Address avps here when
@@ -359,7 +369,7 @@ handle_info(T, #state{} = State) ->
%% Note that there's no guarantee that the service and transport
%% capabilities are good enough to build a CER/CEA that can be
-%% succesfully encoded. It's not checked at diameter:add_transport/2
+%% successfully encoded. It's not checked at diameter:add_transport/2
%% since this can be called before creating the service.
%% terminate/2
@@ -442,9 +452,18 @@ transition({connection_timeout = T, TPid},
transition({connection_timeout, _}, _) ->
ok;
+%% Requests for acknowledgements to the transport.
+transition({diameter, ack}, S) ->
+ S#state{ack = true};
+
%% Incoming message from the transport.
-transition({diameter, {recv, MsgT}}, S) ->
- incoming(MsgT, S);
+transition({diameter, {recv, Msg}}, S) ->
+ incoming(recv(Msg, S), S);
+
+%% Handler of an incoming request is telling of its existence.
+transition({handler, Pid}, _) ->
+ put_route(Pid),
+ ok;
%% Timeout when still in the same state ...
transition({timeout = T, PS}, #state{state = PS}) ->
@@ -458,7 +477,7 @@ transition({timeout, _}, _) ->
transition({send, Msg}, S) ->
outgoing(Msg, S);
transition({send, Msg, Route}, S) ->
- put_route(Route),
+ route_outgoing(Route),
outgoing(Msg, S);
%% Request for graceful shutdown at remove_transport, stop_service of
@@ -487,12 +506,13 @@ transition({'DOWN', _, process, WPid, _},
transition({'DOWN', _, process, TPid, _},
#state{transport = TPid}
= S) ->
- start_next(S);
+ start_next(S#state{ack = false});
%% Transport has died after connection timeout, or handler process has
%% died.
-transition({'DOWN', _, process, Pid, _}, _) ->
- erase_route(Pid),
+transition({'DOWN', _, process, Pid, _}, #state{transport = TPid}) ->
+ is_reference(erase_route(Pid))
+ andalso send(TPid, false), %% answer not forthcoming
ok;
%% State query.
@@ -502,37 +522,62 @@ transition({state, Pid}, #state{state = S, transport = TPid}) ->
%% Crash on anything unexpected.
-%% put_route/1
-%%
+%% route_outgoing/1
+
%% Map identifiers in an outgoing request to be able to lookup the
%% handler process when the answer is received.
-
-put_route({Pid, Ref, Seqs}) ->
+route_outgoing({Pid, Ref, Seqs}) -> %% request
MRef = monitor(process, Pid),
put(Pid, Seqs),
- put(Seqs, {Pid, Ref, MRef}).
+ put(Seqs, {Pid, Ref, MRef});
+
+%% Remove a mapping made for an incoming request.
+route_outgoing(Pid)
+ when is_pid(Pid) -> %% answer
+ MRef = erase_route(Pid),
+ undefined == MRef orelse demonitor(MRef).
+
+%% put_route/1
-%% get_route/1
+%% Monitor on a handler process for an incoming request.
+put_route(Pid) ->
+ MRef = monitor(process, Pid),
+ put(Pid, MRef).
+
+%% get_route/3
-get_route(#diameter_packet{header = #diameter_header{is_request = false}}
- = Pkt) ->
+%% Incoming answer.
+get_route(_, _, #diameter_packet{header = #diameter_header{is_request = false}}
+ = Pkt) ->
Seqs = diameter_codec:sequence_numbers(Pkt),
case erase(Seqs) of
{Pid, Ref, MRef} ->
demonitor(MRef),
erase(Pid),
{Pid, Ref, self()};
- undefined ->
+ undefined -> %% request unknown
false
end;
-get_route(_) ->
- false.
+%% Requests answered here ...
+get_route(_, N, _)
+ when N == 'CER';
+ N == 'DPR' ->
+ false;
+
+%% ... or not.
+get_route(Ack, _, _) ->
+ Ack.
%% erase_route/1
erase_route(Pid) ->
- erase(erase(Pid)).
+ case erase(Pid) of
+ {_,_} = Seqs ->
+ erase(Seqs);
+ T ->
+ T
+ end.
%% capx/1
@@ -560,7 +605,8 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
mode = {connect, Remote},
service = #diameter_service{capabilities = LCaps},
transport = TPid,
- dictionary = Dict}
+ dictionary = Dict,
+ codec = Opts}
= S) ->
OH = LCaps#diameter_caps.origin_host,
req_send_CER(OH, Remote)
@@ -570,7 +616,7 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
#diameter_packet{header = #diameter_header{end_to_end_id = Eid,
hop_by_hop_id = Hid}}
= Pkt
- = encode(CER, Dict),
+ = encode(CER, Opts, Dict),
incr(send, Pkt, Dict),
send(TPid, Pkt),
?LOG(send, 'CER'),
@@ -599,131 +645,126 @@ build_CER(#state{service = #diameter_service{capabilities = LCaps},
{ok, CER} = diameter_capx:build_CER(LCaps, Dict),
CER.
-%% encode/2
+%% encode/3
-encode(Rec, Dict) ->
+encode(Rec, Opts, Dict) ->
Seq = diameter_session:sequence({_,_} = getr(?SEQUENCE_KEY)),
Hdr = #diameter_header{version = ?DIAMETER_VERSION,
end_to_end_id = Seq,
hop_by_hop_id = Seq},
- diameter_codec:encode(Dict, #diameter_packet{header = Hdr,
- msg = Rec}).
+ diameter_codec:encode(Dict, Opts, #diameter_packet{header = Hdr,
+ msg = Rec}).
%% incoming/2
-incoming({Msg, NPid}, S) ->
- try recv(Msg, S) of
- T ->
- NPid ! {diameter, discard},
- T
- catch
- {?MODULE, Name, Pkt} ->
- incoming(Name, Pkt, NPid, S)
- end;
+incoming(#diameter_header{is_request = R}, #state{transport = TPid,
+ ack = Ack}) ->
+ R andalso Ack andalso send(TPid, false),
+ ok;
-incoming(Msg, S) ->
- try
- recv(Msg, S)
- catch
- {?MODULE, Name, Pkt} ->
- incoming(Name, Pkt, false, S)
- end.
+incoming(<<_:32, 1:1, _/bits>>, #state{ack = true} = S) ->
+ send(S#state.transport, false),
+ ok;
-%% incoming/4
+incoming(<<_/bits>>, _) ->
+ ok;
-incoming(Name, Pkt, NPid, #state{parent = Pid} = S) ->
- Pid ! {recv, self(), get_route(Pkt), Name, Pkt, NPid},
- rcv(Name, Pkt, S).
+incoming(T, _) ->
+ T.
%% recv/2
-recv(#diameter_packet{header = #diameter_header{} = Hdr}
- = Pkt,
- #state{dictionary = Dict0}
- = S) ->
- recv1(diameter_codec:msg_name(Dict0, Hdr), Pkt, S);
-
-recv(#diameter_packet{header = undefined,
- bin = Bin}
- = Pkt,
- S) ->
- recv(diameter_codec:decode_header(Bin), Pkt, S);
+recv(#diameter_packet{bin = Bin} = Pkt, S) ->
+ recv(Bin, Pkt, S);
recv(Bin, S) ->
- recv(#diameter_packet{bin = Bin}, S).
+ recv(Bin, Bin, S).
+
+%% recv/3
-%% recv1/3
+recv(Bin, Msg, S) ->
+ recv(diameter_codec:decode_header(Bin), Bin, Msg, S).
+
+%% recv/4
+
+recv(false, Bin, _, #state{length_errors = E}) ->
+ invalid(E, truncated_header, Bin),
+ Bin;
+
+recv(#diameter_header{length = Len} = H, Bin, Msg, #state{length_errors = E,
+ incoming_maxlen = M,
+ dictionary = Dict0}
+ = S)
+ when E == handle;
+ 0 == Len rem 4, bit_size(Bin) == 8*Len, size(Bin) =< M ->
+ recv1(diameter_codec:msg_name(Dict0, H), H, Msg, S);
-recv1(_,
- #diameter_packet{header = H, bin = Bin},
- #state{incoming_maxlen = M})
+recv(H, Bin, _, #state{incoming_maxlen = M})
when M < size(Bin) ->
- invalid(false, incoming_maxlen_exceeded, {size(Bin), H});
+ invalid(false, incoming_maxlen_exceeded, {size(Bin), H}),
+ H;
+
+recv(H, Bin, _, #state{length_errors = E}) ->
+ T = {size(Bin), bit_size(Bin) rem 8, H},
+ invalid(E, message_length_mismatch, T),
+ H.
+
+%% recv1/4
%% Ignore anything but an expected CER/CEA if so configured. This is
%% non-standard behaviour.
-recv1(Name, _, #state{state = {'Wait-CEA', _, _},
- strict = false})
+recv1(Name, H, _, #state{state = {'Wait-CEA', _, _},
+ strict = false})
when Name /= 'CEA' ->
- ok;
-recv1(Name, _, #state{state = recv_CER,
- strict = false})
+ H;
+recv1(Name, H, _, #state{state = recv_CER,
+ strict = false})
when Name /= 'CER' ->
- ok;
+ H;
%% Incoming request after outgoing DPR: discard. Don't discard DPR, so
%% both ends don't do so when sending simultaneously.
-recv1(Name,
- #diameter_packet{header = #diameter_header{is_request = true} = H},
- #state{dpr = {_,_,_}})
+recv1(Name, #diameter_header{is_request = true} = H, _, #state{dpr = {_,_,_}})
when Name /= 'DPR' ->
- invalid(false, recv_after_outgoing_dpr, H);
+ invalid(false, recv_after_outgoing_dpr, H),
+ H;
%% Incoming request after incoming DPR: discard.
-recv1(_,
- #diameter_packet{header = #diameter_header{is_request = true} = H},
- #state{dpr = true}) ->
- invalid(false, recv_after_incoming_dpr, H);
+recv1(_, #diameter_header{is_request = true} = H, _, #state{dpr = true}) ->
+ invalid(false, recv_after_incoming_dpr, H),
+ H;
%% DPA with identifier mismatch, or in response to a DPR initiated by
%% the service.
-recv1('DPA' = N,
- #diameter_packet{header = #diameter_header{hop_by_hop_id = Hid,
- end_to_end_id = Eid}}
- = Pkt,
- #state{dpr = {X,H,E}}
+recv1('DPA' = Name,
+ #diameter_header{hop_by_hop_id = Hid, end_to_end_id = Eid}
+ = H,
+ Msg,
+ #state{dpr = {X,HI,EI}}
= S)
- when H /= Hid;
- E /= Eid;
+ when HI /= Hid;
+ EI /= Eid;
not X ->
- rcv(N, Pkt, S);
+ Pkt = pkt(H, Msg),
+ handle(Name, Pkt, S);
-%% Any other message with a header and no length errors: send to the
-%% parent.
-recv1(Name, Pkt, #state{}) ->
- throw({?MODULE, Name, Pkt}).
+%% Any other message with a header and no length errors.
+recv1(Name, H, Msg, #state{parent = Pid, ack = Ack} = S) ->
+ Pkt = pkt(H, Msg),
+ Pid ! {recv, self(), get_route(Ack, Name, Pkt), Name, Pkt},
+ handle(Name, Pkt, S).
-%% recv/3
+%% pkt/2
-recv(#diameter_header{length = Len}
- = H,
- #diameter_packet{bin = Bin}
- = Pkt,
- #state{length_errors = E}
- = S)
- when E == handle;
- 0 == Len rem 4, bit_size(Bin) == 8*Len ->
- recv(Pkt#diameter_packet{header = H}, S);
+pkt(H, Bin)
+ when is_binary(Bin) ->
+ #diameter_packet{header = H,
+ bin = Bin};
-recv(#diameter_header{}
- = H,
- #diameter_packet{bin = Bin},
- #state{length_errors = E}) ->
- T = {size(Bin), bit_size(Bin) rem 8, H},
- invalid(E, message_length_mismatch, T);
+pkt(H, Pkt) ->
+ Pkt#diameter_packet{header = H}.
-recv(false, #diameter_packet{bin = Bin}, #state{length_errors = E}) ->
- invalid(E, truncated_header, Bin).
+%% invalid/3
%% Note that counters here only count discarded messages.
invalid(E, Reason, T) ->
@@ -732,67 +773,65 @@ invalid(E, Reason, T) ->
?LOG(Reason, T),
ok.
-%% rcv/3
+%% handle/3
%% Incoming CEA.
-rcv('CEA' = N,
- #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
- hop_by_hop_id = Hid}}
- = Pkt,
- #state{state = {'Wait-CEA', Hid, Eid}}
- = S) ->
+handle('CEA' = N,
+ #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
+ hop_by_hop_id = Hid}}
+ = Pkt,
+ #state{state = {'Wait-CEA', Hid, Eid}}
+ = S) ->
?LOG(recv, N),
handle_CEA(Pkt, S);
%% Incoming CER
-rcv('CER' = N, Pkt, #state{state = recv_CER} = S) ->
+handle('CER' = N, Pkt, #state{state = recv_CER} = S) ->
handle_request(N, Pkt, S);
%% Anything but CER/CEA in a non-Open state is an error, as is
%% CER/CEA in anything but recv_CER/Wait-CEA.
-rcv(Name, _, #state{state = PS})
+handle(Name, _, #state{state = PS})
when PS /= 'Open';
Name == 'CER';
Name == 'CEA' ->
{stop, {Name, PS}};
-rcv('DPR' = N, Pkt, S) ->
+handle('DPR' = N, Pkt, S) ->
handle_request(N, Pkt, S);
%% DPA in response to DPR, with the expected identifiers.
-rcv('DPA' = N,
- #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
- hop_by_hop_id = Hid}
- = H}
- = Pkt,
+handle('DPA' = N,
+ #diameter_packet{header = #diameter_header{end_to_end_id = Eid,
+ hop_by_hop_id = Hid}
+ = H}
+ = Pkt,
#state{dictionary = Dict0,
transport = TPid,
- dpr = {X, Hid, Eid}}) ->
+ dpr = {X, Hid, Eid},
+ codec = Opts}) ->
?LOG(recv, N),
X orelse begin
%% Only count DPA in response to a DPR sent by the
%% service: explicit DPR is counted in the same way
%% as other explicitly sent requests.
incr(recv, H, Dict0),
- incr_rc(recv, diameter_codec:decode(Dict0, Pkt), Dict0)
+ {_, RecPkt} = decode(Dict0, Opts, Pkt),
+ incr_rc(recv, RecPkt, Dict0)
end,
diameter_peer:close(TPid),
{stop, N};
-%% Ignore anything else, an unsolicited DPA in particular. Note that
-%% dpa_timeout deals with the case in which the peer sends the wrong
-%% identifiers in DPA.
-rcv(N, #diameter_packet{header = H}, _)
- when N == 'CER';
- N == 'CEA';
- N == 'DPR';
- N == 'DPA' ->
+%% Ignore an unsolicited DPA in particular. Note that dpa_timeout
+%% deals with the case in which the peer sends the wrong identifiers
+%% in DPA.
+handle('DPA' = N, #diameter_packet{header = H}, _) ->
?LOG(ignored, N),
%% Note that these aren't counted in the normal recv counter.
diameter_stats:incr({diameter_codec:msg_id(H), recv, ignored}),
ok;
-rcv(_, _, _) ->
+handle(_, _, _) ->
ok.
%% incr/3
@@ -839,7 +878,7 @@ outgoing(#diameter_packet{header = #diameter_header{application_id = 0,
invalid(false, dpr_after_dpr, H) %% DPR sent: discard
end;
-%% Explict CER or DWR: discard. These are sent by us.
+%% Explicit CER or DWR: discard. These are sent by us.
outgoing(#diameter_packet{header = #diameter_header{application_id = 0,
cmd_code = C,
is_request = true}
@@ -862,7 +901,7 @@ outgoing(#diameter_packet{header = #diameter_header{is_request = false}}
ok;
%% Outgoing request: discard.
-outgoing(Msg, #state{dpr = {_,_,_}}) ->
+outgoing(Msg, #state{}) ->
invalid(false, send_after_dpr, header(Msg)).
header(#diameter_packet{header = H}) ->
@@ -875,22 +914,37 @@ header(Bin) -> %% DWR
%% Incoming CER or DPR.
handle_request(Name,
- #diameter_packet{header = H} = Pkt,
- #state{dictionary = Dict0} = S) ->
+ #diameter_packet{header = H}
+ = Pkt,
+ #state{dictionary = Dict0,
+ codec = Opts}
+ = S) ->
?LOG(recv, Name),
incr(recv, H, Dict0),
- send_answer(Name, diameter_codec:decode(Dict0, Pkt), S).
+ send_answer(Name, decode(Dict0, Opts, Pkt), S).
+
+%% decode/3
+%%
+%% Decode the message as record for diameter_capx, and in the
+%% configured format for events.
+
+decode(Dict0, Opts, Pkt) ->
+ {diameter_codec:decode(Dict0, Opts, Pkt),
+ diameter_codec:decode(Dict0, Opts#{decode_format := record}, Pkt)}.
%% send_answer/3
-send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) ->
- incr_error(recv, ReqPkt, Dict),
+send_answer(Type, {DecPkt, RecPkt}, #state{transport = TPid,
+ dictionary = Dict,
+ codec = Opts}
+ = S) ->
+ incr_error(recv, RecPkt, Dict),
#diameter_packet{header = H,
transport_data = TD}
- = ReqPkt,
+ = RecPkt,
- {Msg, PostF} = build_answer(Type, ReqPkt, S),
+ {Msg, PostF} = build_answer(Type, DecPkt, RecPkt, S),
%% An answer message clears the R and T flags and retains the P
%% flag. The E flag is set at encode.
@@ -902,7 +956,7 @@ send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) ->
msg = Msg,
transport_data = TD},
- AnsPkt = diameter_codec:encode(Dict, Pkt),
+ AnsPkt = diameter_codec:encode(Dict, Opts, Pkt),
incr(send, AnsPkt, Dict),
incr_rc(send, AnsPkt, Dict),
@@ -918,19 +972,17 @@ eval([F|A], S) ->
eval(T, _) ->
close(T).
-%% build_answer/3
+%% build_answer/4
build_answer('CER',
+ DecPkt,
#diameter_packet{msg = CER,
header = #diameter_header{version
= ?DIAMETER_VERSION,
is_error = false},
- errors = []}
- = Pkt,
+ errors = []},
#state{dictionary = Dict0}
= S) ->
- diameter_codec:setopts([{string_decode, false}]),
-
{SupportedApps, RCaps, CEA} = recv_CER(CER, S),
[RC, IS] = Dict0:'#get-'(['Result-Code', 'Inband-Security-Id'], CEA),
@@ -946,25 +998,25 @@ build_answer('CER',
orelse ?THROW(4003), %% DIAMETER_ELECTION_LOST
caps_cb(Caps)
of
- N -> {cea(CEA, N, Dict0), [fun open/5, Pkt,
+ N -> {cea(CEA, N, Dict0), [fun open/5, DecPkt,
SupportedApps,
Caps,
{accept, inband_security(IS)}]}
catch
?FAILURE(Reason) ->
- rejected(Reason, {'CER', Reason, Caps, Pkt}, S)
+ rejected(Reason, {'CER', Reason, Caps, DecPkt}, S)
end;
%% The error checks below are similar to those in diameter_traffic for
%% other messages. Should factor out the commonality.
build_answer(Type,
+ DecPkt,
#diameter_packet{header = H,
- errors = Es}
- = Pkt,
+ errors = Es},
S) ->
{RC, FailedAVP} = result_code(Type, H, Es),
- {answer(Type, RC, FailedAVP, S), post(Type, RC, Pkt, S)}.
+ {answer(Type, RC, FailedAVP, S), post(Type, RC, DecPkt, S)}.
inband_security([]) ->
?NO_INBAND_SECURITY;
@@ -1131,19 +1183,15 @@ recv_CER(CER, #state{service = Svc, dictionary = Dict}) ->
handle_CEA(#diameter_packet{header = H}
= Pkt,
#state{dictionary = Dict0,
- service = #diameter_service{capabilities = LCaps}}
+ service = #diameter_service{capabilities = LCaps},
+ codec = Opts}
= S) ->
incr(recv, H, Dict0),
- #diameter_packet{}
- = DPkt
- = diameter_codec:decode(Dict0, Pkt),
-
- diameter_codec:setopts([{string_decode, false}]),
-
- RC = result_code(incr_rc(recv, DPkt, Dict0)),
+ {DecPkt, RecPkt} = decode(Dict0, Opts, Pkt),
- {SApps, IS, RCaps} = recv_CEA(DPkt, S),
+ RC = result_code(incr_rc(recv, RecPkt, Dict0)),
+ {SApps, IS, RCaps} = recv_CEA(RecPkt, S),
#diameter_caps{origin_host = {OH, DH}}
= Caps
@@ -1166,9 +1214,9 @@ handle_CEA(#diameter_packet{header = H}
orelse ?THROW(election_lost),
caps_cb(Caps)
of
- _ -> open(DPkt, SApps, Caps, {connect, hd([_] = IS)}, S)
+ _ -> open(DecPkt, SApps, Caps, {connect, hd([_] = IS)}, S)
catch
- ?FAILURE(Reason) -> close({'CEA', Reason, Caps, DPkt})
+ ?FAILURE(Reason) -> close({'CEA', Reason, Caps, DecPkt})
end.
%% Check more than the result code since the peer could send success
%% regardless. If not 2001 then a peer_up callback could do anything
@@ -1330,8 +1378,9 @@ dpr([], [Reason | _], S) ->
-record(opts, {cause, timeout}).
-send_dpr(Reason, Opts, #state{dictionary = Dict,
- service = #diameter_service{capabilities = Caps}}
+send_dpr(Reason, DprOpts, #state{dictionary = Dict,
+ service = #diameter_service{capabilities = Caps},
+ codec = Opts}
= S) ->
#opts{cause = Cause, timeout = Tmo}
= lists:foldl(fun opt/2,
@@ -1340,7 +1389,7 @@ send_dpr(Reason, Opts, #state{dictionary = Dict,
_ -> ?REBOOT
end,
timeout = dpa_timeout()},
- Opts),
+ DprOpts),
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}
= Caps,
@@ -1348,6 +1397,7 @@ send_dpr(Reason, Opts, #state{dictionary = Dict,
Pkt = encode(['DPR', {'Origin-Host', OH},
{'Origin-Realm', OR},
{'Disconnect-Cause', Cause}],
+ Opts,
Dict),
send_dpr(false, Pkt, Tmo, S).
diff --git a/lib/diameter/src/base/diameter_reg.erl b/lib/diameter/src/base/diameter_reg.erl
index 9027130063..c1762a07e3 100644
--- a/lib/diameter/src/base/diameter_reg.erl
+++ b/lib/diameter/src/base/diameter_reg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,10 +19,11 @@
%%
%%
-%% The module implements a simple term -> pid registry.
+%% A simple term -> pid registry.
%%
-module(diameter_reg).
+
-behaviour(gen_server).
-export([add/1,
@@ -57,18 +58,18 @@
-type key() :: term().
-type from() :: {pid(), term()}.
+-type rcvr() :: [pid() | term()] %% subscribe
+ | from(). %% wait
-type pattern() :: term().
-record(state, {id = diameter_lib:now(),
- receivers = dict:new()
- :: dict:dict(pattern(), [[pid() | term()]%% subscribe
- | from()]), %% wait
+ notify = #{} :: #{pattern() => [rcvr()]},
monitors = sets:new() :: sets:set(pid())}).
%% The ?TABLE bag contains the Key -> Pid mapping, as {Key, Pid}
%% tuples. Each pid is stored in the monitors set to ensure only one
%% monitor for each pid: more are harmless, but unnecessary. A pattern
-%% is added to receivers a result of calls to wait/1 or subscribe/2:
+%% is added to notify a result of calls to wait/1 or subscribe/2:
%% changes to ?TABLE causes processes to be notified as required.
%% ===========================================================================
@@ -137,7 +138,7 @@ match(Pat) ->
match(Pat, Pid) ->
ets:match_object(?TABLE, {Pat, Pid}).
-
+
%% ===========================================================================
%% # wait(Pat)
%%
@@ -156,7 +157,7 @@ wait(Pat) ->
%% # subscribe(Pat, T)
%%
%% Like match/1, but additionally receive messages of the form
-%% {T, add|remove, {term(), pid()} when associations are added
+%% {T, add|remove, {term(), pid()}} when associations are added
%% or removed.
%% ===========================================================================
@@ -186,15 +187,12 @@ uptime() ->
-> [{pid(), [key()]}].
pids() ->
- to_list(fun swap/1).
-
-to_list(Fun) ->
- ets:foldl(fun(T,D) -> append(Fun(T), D) end, orddict:new(), ?TABLE).
+ append(ets:select(?TABLE, [{{'$1','$2'}, [], [{{'$2', '$1'}}]}])).
-append({K,V}, Dict) ->
- orddict:append(K, V, Dict).
-
-id(T) -> T.
+append(Pairs) ->
+ dict:to_list(lists:foldl(fun({K,V}, D) -> dict:append(K, V, D) end,
+ dict:new(),
+ Pairs)).
%% terms/0
@@ -202,9 +200,7 @@ id(T) -> T.
-> [{key(), [pid()]}].
terms() ->
- to_list(fun id/1).
-
-swap({X,Y}) -> {Y,X}.
+ append(ets:tab2list(?TABLE)).
%% subs/0
@@ -212,31 +208,19 @@ swap({X,Y}) -> {Y,X}.
-> [{pattern(), [{pid(), term()}]}].
subs() ->
- #state{receivers = RD} = state(),
- dict:fold(fun sub/3, orddict:new(), RD).
-
-sub(Pat, Ps, Dict) ->
- lists:foldl(fun([P|T], D) -> orddict:append(Pat, {P,T}, D);
- (_, D) -> D
- end,
- Dict,
- Ps).
+ #state{notify = Dict} = state(),
+ [{K, Ts} || {K,Ps} <- maps:to_list(Dict),
+ Ts <- [[{P,T} || [P|T] <- Ps]]].
%% waits/0
-spec waits()
- -> [{pattern(), [{from(), term()}]}].
+ -> [{pattern(), [from()]}].
waits() ->
- #state{receivers = RD} = state(),
- dict:fold(fun wait/3, orddict:new(), RD).
-
-wait(Pat, Ps, Dict) ->
- lists:foldl(fun({_,_} = F, D) -> orddict:append(Pat, F, D);
- (_, D) -> D
- end,
- Dict,
- Ps).
+ #state{notify = Dict} = state(),
+ [{K, Ts} || {K,Ps} <- maps:to_list(Dict),
+ Ts <- [[T || {_,_} = T <- Ps]]].
%% ----------------------------------------------------------
%% # init/1
@@ -250,33 +234,35 @@ init(_) ->
%% # handle_call/3
%% ----------------------------------------------------------
-handle_call({add, Uniq, Key}, {Pid, _}, S0) ->
+handle_call({add, Uniq, Key}, {Pid, _}, S) ->
Rec = {Key, Pid},
- S1 = flush(Uniq, Rec, S0),
+ NS = flush(Uniq, Rec, S), %% before insert
{Res, New} = insert(Uniq, Rec),
- {Recvs, S} = add(New, Rec, S1),
- notify(Recvs, Rec),
- {reply, Res, S};
+ {reply, Res, notify(add, New andalso Rec, if New ->
+ add_monitor(Pid, NS);
+ true ->
+ NS
+ end)};
handle_call({remove, Key}, {Pid, _}, S) ->
Rec = {Key, Pid},
- Recvs = delete([Rec], S),
- ets:delete_object(?TABLE, Rec),
- notify(Recvs, remove),
- {reply, true, S};
+ {reply, true, try
+ notify(remove, Rec, S)
+ after
+ ets:delete_object(?TABLE, Rec)
+ end};
-handle_call({wait, Pat}, {Pid, _} = From, #state{receivers = RD} = S) ->
+handle_call({wait, Pat}, {Pid, _} = From, S) ->
NS = add_monitor(Pid, S),
case match(Pat) of
- [_|_] = L ->
- {reply, L, NS};
+ [_|_] = Recs ->
+ {reply, Recs, NS};
[] ->
- {noreply, NS#state{receivers = dict:append(Pat, From, RD)}}
+ {noreply, queue(Pat, From, NS)}
end;
-handle_call({subscribe, Pat, T}, {Pid, _}, #state{receivers = RD} = S) ->
- NS = add_monitor(Pid, S),
- {reply, match(Pat), NS#state{receivers = dict:append(Pat, [Pid | T], RD)}};
+handle_call({subscribe, Pat, T}, {Pid, _}, S) ->
+ {reply, match(Pat), queue(Pat, [Pid | T], add_monitor(Pid, S))};
handle_call(state, _, S) ->
{reply, S, S};
@@ -315,6 +301,11 @@ terminate(_Reason, _State)->
%% # code_change/3
%% ----------------------------------------------------------
+code_change(_, State, "2.1") ->
+ {ok, lists:foldl(fun add_monitor/2,
+ State,
+ ets:select(?TABLE, [{{'_', '$1'}, [], ['$1']}]))};
+
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
@@ -332,106 +323,62 @@ insert(true, Rec) ->
B = ets:insert_new(?TABLE, Rec), %% entry inserted?
{B, B}.
-%% add/3
-
+%% add_monitor/2
+%%
%% Only add a single monitor for any given process, since there's no
%% use to more.
-add(true, {_Key, Pid} = Rec, S) ->
- NS = add_monitor(Pid, S),
- {Recvs, RD} = add(Rec, NS),
- {Recvs, S#state{receivers = RD}};
-
-add(false = No, _, S) ->
- {No, S}.
-
-%% add/2
-
-%% Notify processes whose patterns match the inserted key.
-add({_Key, Pid} = Rec, #state{receivers = RD}) ->
- dict:fold(fun(Pt, Ps, A) ->
- add(lists:member(Rec, match(Pt, Pid)), Pt, Ps, Rec, A)
- end,
- {sets:new(), RD},
- RD).
-
-%% add/5
-add(true, Pat, Recvs, {_,_} = Rec, {Set, Dict}) ->
- {lists:foldl(fun sets:add_element/2, Set, Recvs),
- remove(fun erlang:is_list/1, Pat, Recvs, Dict)};
+add_monitor(Pid, #state{monitors = Ps} = S) ->
+ case sets:is_element(Pid, Ps) of
+ false ->
+ monitor(process, Pid),
+ S#state{monitors = sets:add_element(Pid, Ps)};
+ true ->
+ S
+ end.
-add(false, _, _, _, Acc) ->
- Acc.
+%% notify/3
-%% add_monitor/2
-
-add_monitor(Pid, #state{monitors = MS} = S) ->
- add_monitor(sets:is_element(Pid, MS), Pid, S).
-
-%% add_monitor/3
-
-add_monitor(false, Pid, #state{monitors = MS} = S) ->
- monitor(process, Pid),
- S#state{monitors = sets:add_element(Pid, MS)};
-
-add_monitor(true, _, S) ->
- S.
-
-%% delete/2
-
-delete(Recs, #state{receivers = RD}) ->
- lists:foldl(fun(R,S) -> delete(R, RD, S) end, sets:new(), Recs).
-
-%% delete/3
+notify(_, false, S) ->
+ S;
-delete({_Key, Pid} = Rec, RD, Set) ->
- dict:fold(fun(Pt, Ps, S) ->
- delete(lists:member(Rec, match(Pt, Pid)), Rec, Ps, S)
- end,
- Set,
- RD).
+notify(Op, {_,_} = Rec, #state{notify = Dict} = S) ->
+ S#state{notify = maps:fold(fun(P,Rs,D) -> notify(Op, Rec, P, Rs, D) end,
+ Dict,
+ Dict)}.
-%% delete/4
+%% notify/5
-%% Entry matches a pattern ...
-delete(true, Rec, Recvs, Set) ->
- lists:foldl(fun(R,S) -> sets:add_element({R, Rec}, S) end,
- Set,
- Recvs);
-
-%% ... or not.
-delete(false, _, _, Set) ->
- Set.
-
-%% notify/2
-
-notify(false = No, _) ->
- No;
-
-notify(Recvs, remove = Op) ->
- sets:fold(fun({P,R}, N) -> send(P, R, Op), N+1 end, 0, Recvs);
-
-notify(Recvs, {_,_} = Rec) ->
- sets:fold(fun(P,N) -> send(P, Rec, add), N+1 end, 0, Recvs).
+notify(Op, {_, Pid} = Rec, Pat, Rcvrs, Dict) ->
+ case lists:member(Rec, match(Pat, Pid)) of
+ true ->
+ reset(Pat, Dict, [P || P <- Rcvrs, send(P, Op, Rec)]);
+ false ->
+ Dict
+ end.
%% send/3
-%% No processes waiting on remove, by construction: they've either
-%% received notification at add or aren't waiting.
-send([Pid | T], Rec, Op) ->
- Pid ! {T, Op, Rec};
+send([Pid | T], Op, Rec) ->
+ Pid ! {T, Op, Rec},
+ true;
-send({_,_} = From, Rec, add) ->
- gen_server:reply(From, [Rec]).
+%% No processes wait on remove: they receive notification immediately
+%% or at add, by construction.
+send({_,_} = From, add, Rec) ->
+ gen_server:reply(From, [Rec]),
+ false.
%% down/2
-down(Pid, #state{monitors = MS} = S) ->
- NS = flush(Pid, S),
- Recvs = delete(match('_', Pid), NS),
- ets:match_delete(?TABLE, {'_', Pid}),
- notify(Recvs, remove),
- NS#state{monitors = sets:del_element(Pid, MS)}.
+down(Pid, #state{monitors = Ps} = S) ->
+ Recs = match('_', Pid),
+ Acc0 = flush(Pid, S#state{monitors = sets:del_element(Pid, Ps)}),
+ try
+ lists:foldl(fun(R,NS) -> notify(remove, R, NS) end, Acc0, Recs)
+ after
+ ets:match_delete(?TABLE, {'_', Pid})
+ end.
%% flush/3
@@ -452,16 +399,15 @@ flush(false, _, S) ->
%% flush/2
%% Process has died and should no longer receive messages/replies.
-flush(Pid, #state{receivers = RD} = S)
- when is_pid(Pid) ->
- S#state{receivers = dict:fold(fun(Pt,Ps,D) -> flush(Pid, Pt, Ps, D) end,
- RD,
- RD)}.
+flush(Pid, #state{notify = Dict} = S) ->
+ S#state{notify = maps:fold(fun(P,Rs,D) -> flush(Pid, P, Rs, D) end,
+ Dict,
+ Dict)}.
%% flush/4
-flush(Pid, Pat, Recvs, Dict) ->
- remove(fun(T) -> Pid /= head(T) end, Pat, Recvs, Dict).
+flush(Pid, Pat, Rcvrs, Dict) ->
+ reset(Pat, Dict, [T || T <- Rcvrs, Pid /= head(T)]).
%% head/1
@@ -471,15 +417,18 @@ head([P|_]) ->
head({P,_}) ->
P.
-%% remove/4
+%% reset/3
+
+reset(Key, Map, []) ->
+ maps:remove(Key, Map);
+
+reset(Key, Map, List) ->
+ maps:put(Key, List, Map).
+
+%% queue/3
-remove(Pred, Key, Values, Dict) ->
- case lists:filter(Pred, Values) of
- [] ->
- dict:erase(Key, Dict);
- Rest ->
- dict:store(Key, Rest, Dict)
- end.
+queue(Pat, Rcvr, #state{notify = Dict} = S) ->
+ S#state{notify = maps:put(Pat, [Rcvr | maps:get(Pat, Dict, [])], Dict)}.
%% call/1
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index e4f77e3a24..77d184cfc7 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -88,12 +88,6 @@
%% outside of the service process.
-define(STATE_TABLE, ?MODULE).
-%% The default sequence mask.
--define(NOMASK, {0,32}).
-
-%% The default restrict_connections.
--define(RESTRICT, nodes).
-
%% Workaround for dialyzer's lack of understanding of match specs.
-type match(T)
:: T | '_' | '$1' | '$2'.
@@ -110,21 +104,33 @@
service :: #diameter_service{},
watchdogT = ets_new(watchdogs) %% #watchdog{} at start
:: ets:tid(),
- peerT, %% undefined in new code, but remain for upgrade
- shared_peers, %% reasons. Replaced by local/remote.
- local_peers, %%
local :: {ets:tid(), ets:tid(), ets:tid()},
remote :: {ets:tid(), ets:tid(), ets:tid()},
monitor = false :: false | pid(), %% process to die with
- options
- :: [{sequence, diameter:sequence()} %% sequence mask
- | {share_peers, diameter:remotes()} %% broadcast to
- | {use_shared_peers, diameter:remotes()} %% use from
- | {restrict_connections, diameter:restriction()}
- | {strict_mbit, boolean()}
- | {string_decode, boolean()}
- | {incoming_maxlen, diameter:message_length()}]}).
-%% shared_peers reflects the peers broadcast from remote nodes.
+ options :: #{sequence := diameter:sequence(), %% sequence mask
+ share_peers := diameter:remotes(),%% broadcast to
+ use_shared_peers := diameter:remotes(),%% use from
+ restrict_connections := diameter:restriction(),
+ incoming_maxlen := diameter:message_length(),
+ strict_arities => diameter:strict_arities(),
+ strict_mbit := boolean(),
+ decode_format := diameter:decode_format(),
+ avp_dictionaries => nonempty_list(module()),
+ traffic_counters := boolean(),
+ string_decode := boolean(),
+ capabilities_cb => diameter:evaluable(),
+ pool_size => pos_integer(),
+ capx_timeout => diameter:'Unsigned32'(),
+ strict_capx => boolean(),
+ disconnect_cb => diameter:evaluable(),
+ dpr_timeout => diameter:'Unsigned32'(),
+ dpa_timeout => diameter:'Unsigned32'(),
+ length_errors => exit | handle | discard,
+ connect_timer => diameter:'Unsigned32'(),
+ watchdog_timer => diameter:'Unsigned32'()
+ | {module(), atom(), list()},
+ watchdog_config => [{okay|suspect, non_neg_integer()}],
+ spawn_opt := list() | {module(), atom(), list()}}}).
%% Record representing an RFC 3539 watchdog process implemented by
%% diameter_watchdog.
@@ -145,7 +151,7 @@
apps :: match([{0..16#FFFFFFFF, diameter:app_alias()}] %% {Id, Alias}
| [diameter:app_alias()]), %% remote
caps :: match(#diameter_caps{}),
- started = diameter_lib:now(), %% at process start or sharing
+ started = diameter_lib:now(), %% at connection_up
watchdog :: match(pid() %% key into watchdogT
| undefined)}). %% undefined if remote
@@ -284,7 +290,7 @@ whois(SvcName) ->
%% ---------------------------------------------------------------------------
-spec pick_peer(SvcName, AppOrAlias, Opts)
- -> {{TPid, Caps, App}, Mask, SvcOpts}
+ -> {{{TPid, Caps}, App}, SvcOpts}
| false %% no selection
| {error, no_service}
when SvcName :: diameter:service_name(),
@@ -292,14 +298,12 @@ whois(SvcName) ->
| {alias, diameter:app_alias()},
Opts :: {fun((Dict :: module()) -> [term()]),
diameter:peer_filter(),
- Xtra :: list()},
+ Xtra :: list(),
+ [diameter:peer_ref()]},
TPid :: pid(),
Caps :: #diameter_caps{},
App :: #diameter_app{},
- Mask :: diameter:sequence(),
- SvcOpts :: [diameter:service_opt()].
-%% Extract Mask in the returned tuple so that diameter_traffic doesn't
-%% need to know about the ordering of SvcOpts used here.
+ SvcOpts :: map().
pick_peer(SvcName, App, Opts) ->
pick(lookup_state(SvcName), App, Opts).
@@ -319,16 +323,16 @@ pick(#state{service = #diameter_service{applications = Apps}}
pick(_, false = No, _) ->
No;
-pick(#state{options = [{_, Mask} | SvcOpts]}
+pick(#state{options = SvcOpts}
= S,
#diameter_app{module = ModX, dictionary = Dict}
= App0,
- {DestF, Filter, Xtra}) ->
+ {DestF, Filter, Xtra, TPids}) ->
App = App0#diameter_app{module = ModX ++ Xtra},
[_,_] = RealmAndHost = diameter_lib:eval([DestF, Dict]),
- case pick_peer(App, RealmAndHost, Filter, S) of
- {TPid, Caps} ->
- {{TPid, Caps, App}, Mask, SvcOpts};
+ case pick_peer(App, RealmAndHost, [Filter | TPids], S) of
+ {_TPid, _Caps} = TC ->
+ {{TC, App}, SvcOpts};
false = No ->
No
end.
@@ -526,6 +530,13 @@ transition({tc_timeout, T}, S) ->
tc_timeout(T, S),
ok;
+transition({nodeup, Node, _}, S) ->
+ nodeup(Node, S),
+ ok;
+
+transition({nodedown, _Node, _}, _) ->
+ ok;
+
transition(Req, S) ->
unexpected(handle_info, [Req], S),
ok.
@@ -543,94 +554,32 @@ terminate(Reason, #state{service_name = Name, local = {PeerT, _, _}} = S) ->
%% wait for watchdog state changes to take care of if. That this
%% takes place after deleting the state entry ensures that the
%% resulting failover by request processes accomplishes nothing.
- ets:foldl(fun(#peer{pid = TPid}, _) ->
- diameter_traffic:peer_down(TPid)
- end,
- ok,
- PeerT),
+ ets:foldl(fun peer_down/2, ok, PeerT),
shutdown == Reason %% application shutdown
andalso shutdown(application, S).
+%% peer_down/1
+%%
+%% Entries with watchdog state SUSPECT are already down: ignore the
+%% expected failure. This assumes the current implementation, but
+%% double the number of lookups (in the typical case) could be the
+%% greater evil if there are many peer connections.
+
+peer_down(#peer{pid = TPid}, _) ->
+ try
+ diameter_traffic:peer_down(TPid)
+ catch
+ error: {badmatch, []} -> ok
+ end.
+
%% ---------------------------------------------------------------------------
%% # code_change/3
%% ---------------------------------------------------------------------------
-code_change(_FromVsn, #state{} = S, _Extra) ->
- {ok, S};
-
-%% Don't support downgrade since we won't in appup.
-code_change({down = T, _}, _, _Extra) ->
- {error, T};
-
-%% Upgrade local/shared peers dicts populated in old code. Don't
-code_change(_FromVsn, S0, _Extra) ->
- {state, Id, SvcName, Svc, WT, PeerT, SDict, LDict, Monitor, Opts}
- = S0,
-
- init_peers(LT = setelement(1, {PT, _, _} = init_peers(), PeerT),
- fun({_,A}) -> A end),
- init_peers(init_peers(RT = init_peers(), SDict),
- fun(A) -> A end),
-
- S = #state{id = Id,
- service_name = SvcName,
- service = Svc,
- watchdogT = WT,
- peerT = PT, %% empty
- shared_peers = SDict,
- local_peers = LDict,
- local = LT,
- remote = RT,
- monitor = Monitor,
- options = Opts},
-
- %% Replacing the table entry and deleting the old shared tables
- %% can make outgoing requests return {error, no_connection} until
- %% everyone is running new code. Don't delete the tables to avoid
- %% crashing request processes.
- ets:delete_all_objects(SDict),
- ets:delete_all_objects(LDict),
- ets:insert(?STATE_TABLE, S),
+code_change(_FromVsn, S, _Extra) ->
{ok, S}.
-%% init_peers/2
-
-%% Populate app and identity bags from a new-style #peer{} sets.
-init_peers({PeerT, _, _} = T, F)
- when is_function(F) ->
- ets:foldl(fun(#peer{pid = P, apps = As, caps = C}, N) ->
- insert_peer(P, lists:map(F, As), C, T),
- N+1
- end,
- 0,
- PeerT);
-
-%% Populate #peer{} table given a shared peers dict.
-init_peers({PeerT, _, _}, SDict) ->
- dict:fold(fun(P, As, N) ->
- ets:update_element(PeerT, P, {#peer.apps, As}),
- N+1
- end,
- 0,
- diameter_dict:fold(fun(A, Ps, D) ->
- init_peers(A, Ps, PeerT, D)
- end,
- dict:new(),
- SDict)).
-
-%% init_peers/4
-
-init_peers(App, TCs, PeerT, Dict) ->
- lists:foldl(fun({P,C}, D) ->
- ets:insert(PeerT, #peer{pid = P,
- apps = [],
- caps = C}),
- dict:append(P, App, D)
- end,
- Dict,
- TCs).
-
%% ===========================================================================
%% ===========================================================================
@@ -763,12 +712,15 @@ i(SvcName) ->
cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
{false, Acc}) ->
lists:foreach(fun init_mod/1, Apps),
+ #{monitor := M}
+ = SvcOpts
+ = service_opts(Opts),
S = #state{service_name = SvcName,
service = Rec#diameter_service{pid = self()},
local = init_peers(),
remote = init_peers(),
- monitor = mref(get_value(monitor, Opts)),
- options = service_options(Opts)},
+ monitor = mref(M),
+ options = maps:remove(monitor, SvcOpts)},
{S, Acc};
cfg_acc({_Ref, Type, _Opts} = T, {S, Acc})
@@ -783,26 +735,39 @@ init_peers() ->
%% Alias,
%% TPid}
-service_options(Opts) ->
- [{sequence, proplists:get_value(sequence, Opts, ?NOMASK)},
- {share_peers, get_value(share_peers, Opts)},
- {use_shared_peers, get_value(use_shared_peers, Opts)},
- {restrict_connections, proplists:get_value(restrict_connections,
- Opts,
- ?RESTRICT)},
- {spawn_opt, proplists:get_value(spawn_opt, Opts, [])},
- {string_decode, proplists:get_value(string_decode, Opts, true)},
- {incoming_maxlen, proplists:get_value(incoming_maxlen, Opts, 16#FFFFFF)},
- {strict_mbit, proplists:get_value(strict_mbit, Opts, true)}].
-%% The order of options is significant since we match against the list.
+service_opts(Opts) ->
+ remove([{strict_arities, true},
+ {avp_dictionaries, []}],
+ maps:merge(maps:from_list([{monitor, false} | def_opts()]),
+ maps:from_list(Opts))).
+
+remove(List, Map) ->
+ maps:filter(fun(K,V) -> not lists:member({K,V}, List) end,
+ Map).
+
+def_opts() -> %% defaults on the service map
+ [{share_peers, false},
+ {use_shared_peers, false},
+ {sequence, {0,32}},
+ {restrict_connections, nodes},
+ {incoming_maxlen, 16#FFFFFF},
+ {strict_arities, true},
+ {strict_mbit, true},
+ {decode_format, record},
+ {avp_dictionaries, []},
+ {traffic_counters, true},
+ {string_decode, true},
+ {spawn_opt, []}].
mref(false = No) ->
No;
mref(P) ->
monitor(process, P).
-init_shared(#state{options = [_, _, {_,T} | _],
+init_shared(#state{options = #{use_shared_peers := T},
service_name = Svc}) ->
+ T == false orelse net_kernel:monitor_nodes(true, [{node_type, visible},
+ nodedown_reason]),
notify(T, Svc, {service, self()}).
init_mod(#diameter_app{alias = Alias,
@@ -812,16 +777,17 @@ init_mod(#diameter_app{alias = Alias,
start_fsm({Ref, Type, Opts}, S) ->
start(Ref, {Type, Opts}, S).
-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.
+nodeup(Node, #state{options = #{share_peers := SP},
+ service_name = SvcName}) ->
+ lists:member(Node, remotes(SP))
+ andalso diameter_peer:notify([Node], SvcName, {service, self()}).
+
remotes(false) ->
[];
@@ -842,8 +808,8 @@ remotes(F) ->
error_report(invalid_return, share_peers, F),
[]
catch
- E:R ->
- ?LOG(failure, {E, R, F, diameter_lib:get_stacktrace()}),
+ E:R:S ->
+ ?LOG(failure, {E, R, F, diameter_lib:stacktrace(S)}),
error_report(failure, share_peers, F),
[]
end.
@@ -899,7 +865,8 @@ start(Ref, Type, Opts, State) ->
start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT,
local = {PeerT, _, _},
- options = SvcOpts,
+ options = #{string_decode := SD}
+ = SvcOpts,
service_name = SvcName,
service = Svc0})
when Type == connect;
@@ -907,21 +874,29 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT,
#diameter_service{applications = Apps}
= Svc1
= merge_service(Opts, Svc0),
- Svc = binary_caps(Svc1, proplists:get_value(string_decode, SvcOpts, true)),
- RecvData = diameter_traffic:make_recvdata([SvcName,
- PeerT,
- Apps,
- SvcOpts]),
- T = {{spawn_opts([Opts, SvcOpts]), RecvData}, Opts, SvcOpts, Svc},
+ Svc = binary_caps(Svc1, SD),
+ {SOpts, TOpts} = merge_opts(SvcOpts, Opts),
+ RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, SOpts]),
+ T = {TOpts, SOpts, RecvData, Svc},
Rec = #watchdog{type = Type,
ref = Ref,
- options = Opts},
+ options = TOpts},
+
diameter_lib:fold_n(fun(_,A) ->
[wd(Type, Ref, T, WatchdogT, Rec) | A]
end,
[],
N).
+merge_opts(SvcOpts, Opts) ->
+ Keys = [K || {K,_} <- def_opts()],
+ SO = [T || {K,_} = T <- Opts, lists:member(K, Keys)],
+ TO = Opts -- SO,
+ {maps:merge(maps:with(Keys, SvcOpts), maps:from_list(SO)),
+ TO ++ [T || {K,_} = T <- maps:to_list(SvcOpts),
+ not lists:member(K, Keys),
+ not lists:keymember(K, 1, Opts)]}.
+
binary_caps(Svc, true) ->
Svc;
binary_caps(#diameter_service{capabilities = Caps} = Svc, false) ->
@@ -936,12 +911,6 @@ wd(Type, Ref, T, WatchdogT, Rec) ->
%% record so that each watchdog may get a different record. This
%% record is what is passed back into application callbacks.
-spawn_opts(Optss) ->
- SpawnOpts = get_value(spawn_opt, Optss, []),
- [T || T <- SpawnOpts,
- T /= link,
- T /= monitor].
-
start_watchdog(Type, Ref, T) ->
{_MRef, Pid} = diameter_watchdog:start({Type, Ref}, T),
Pid.
@@ -1154,18 +1123,6 @@ keyfind([Key | Rest], Pos, L) ->
T
end.
-%% get_value/3
-
-get_value(_, [], Def) ->
- Def;
-get_value(Key, [L | Rest], Def) ->
- case lists:keyfind(Key, 1, L) of
- {_,V} ->
- V;
- _ ->
- get_value(Key, Rest, Def)
- end.
-
%% find_outgoing_app/2
find_outgoing_app(Alias, Apps) ->
@@ -1189,11 +1146,11 @@ peer_cb(App, F, A) ->
mod_state(App#diameter_app.alias, ModS),
true
catch
- E:R ->
+ E:R:S ->
%% Don't include arguments since a #diameter_caps{} strings
%% from the peer, which could be anything (especially, large).
[Mod|X] = App#diameter_app.module,
- ?LOG(failure, {E, R, Mod, F, diameter_lib:get_stacktrace()}),
+ ?LOG(failure, {E, R, Mod, F, diameter_lib:stacktrace(S)}),
error_report(failure, F, {Mod, F, A ++ X}),
false
end.
@@ -1419,9 +1376,9 @@ cm([#diameter_app{alias = Alias} = App], Req, From, Svc) ->
?LOG(invalid_return, {ModX, handle_call, Args, T}),
invalid
catch
- E: Reason ->
+ E: Reason: S ->
ModX = App#diameter_app.module,
- Stack = diameter_lib:get_stacktrace(),
+ Stack = diameter_lib:stacktrace(S),
?LOG(failure, {E, Reason, ModX, handle_call, Stack}),
failure
end;
@@ -1463,19 +1420,19 @@ send_event(#diameter_event{service = SvcName} = E) ->
%% # share_peer/5
%% ---------------------------------------------------------------------------
-share_peer(up, Caps, Apps, TPid, #state{options = [_, {_,T} | _],
+share_peer(up, Caps, Apps, TPid, #state{options = #{share_peers := SP},
service_name = Svc}) ->
- notify(T, Svc, {peer, TPid, [A || {_,A} <- Apps], Caps});
+ notify(SP, Svc, {peer, TPid, [A || {_,A} <- Apps], Caps});
-share_peer(down, _Caps, _Apps, TPid, #state{options = [_, {_,T} | _],
+share_peer(down, _Caps, _Apps, TPid, #state{options = #{share_peers := SP},
service_name = Svc}) ->
- notify(T, Svc, {peer, TPid}).
+ notify(SP, Svc, {peer, TPid}).
%% ---------------------------------------------------------------------------
%% # share_peers/2
%% ---------------------------------------------------------------------------
-share_peers(Pid, #state{options = [_, {_,SP} | _],
+share_peers(Pid, #state{options = #{share_peers := SP},
local = {PeerT, AppT, _}}) ->
is_remote(Pid, SP)
andalso ets:foldl(fun(T, N) -> N + sp(Pid, AppT, T) end,
@@ -1507,8 +1464,15 @@ is_remote(Pid, T) ->
%% # remote_peer_up/4
%% ---------------------------------------------------------------------------
-remote_peer_up(TPid, Aliases, Caps, #state{options = [_, _, {_,T} | _]} = S) ->
- is_remote(TPid, T) andalso rpu(TPid, Aliases, Caps, S).
+remote_peer_up(TPid, Aliases, Caps, #state{options = #{use_shared_peers := T},
+ remote = {PeerT, _, _}}
+ = S) ->
+ is_remote(TPid, T)
+ andalso not ets:member(PeerT, TPid)
+ andalso rpu(TPid, Aliases, Caps, S).
+
+%% Notification can be duplicate since remote nodes push and the local
+%% node pulls.
rpu(TPid, Aliases, Caps, #state{service = Svc, remote = RT}) ->
#diameter_service{applications = Apps} = Svc,
@@ -1518,6 +1482,7 @@ rpu(TPid, Aliases, Caps, #state{service = Svc, remote = RT}) ->
rpu(_, [] = No, _, _) ->
No;
+
rpu(TPid, Aliases, Caps, {PeerT, _, _} = RT) ->
monitor(process, TPid),
ets:insert(PeerT, #peer{pid = TPid,
@@ -1620,17 +1585,23 @@ pick_peer(Local,
?LOG(invalid_return, {ModX, pick_peer, T}),
false
catch
- E: Reason when M ->
+ E: Reason: Stack when M ->
ModX = App#diameter_app.module,
- Stack = diameter_lib:get_stacktrace(),
- ?LOG(failure, {E, Reason, ModX, pick_peer, Stack}),
+ Z = diameter_lib:stacktrace(Stack),
+ ?LOG(failure, {E, Reason, ModX, pick_peer, Z}),
false
end.
%% peers/4
-peers(Alias, RH, Filter, T) ->
- filter(Alias, RH, Filter, T, true).
+%% No peer options pointing at specific peers: search for them.
+peers(Alias, RH, [Filter], T) ->
+ filter(Alias, RH, Filter, T, true);
+
+%% Or just lookup.
+peers(_Alias, RH, [Filter | TPids], {PeerT, _AppT, _IdentT}) ->
+ {Ts, _} = filter(caps(PeerT, TPids), RH, Filter),
+ Ts.
%% filter/5
%%
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index bc1ccf4feb..d2856ae530 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -30,7 +30,7 @@
-export([send_request/4]).
%% towards diameter_watchdog
--export([receive_message/6]).
+-export([receive_message/5]).
%% towards diameter_peer_fsm and diameter_watchdog
-export([incr/4,
@@ -54,8 +54,7 @@
-define(RELAY, ?DIAMETER_DICT_RELAY).
-define(BASE, ?DIAMETER_DICT_COMMON). %% Note: the RFC 3588 dictionary
--define(DEFAULT_TIMEOUT, 5000). %% for outgoing requests
--define(DEFAULT_SPAWN_OPTS, []).
+-define(DEFAULT(V, Def), if V == undefined -> Def; true -> V end).
%% Table containing outgoing entries that live and die with
%% peer_up/down. The name is historic, since the table used to contain
@@ -65,20 +64,25 @@
%% Record diameter:call/4 options are parsed into.
-record(options,
- {filter = none :: diameter:peer_filter(),
+ {peers = [] :: [diameter:peer_ref()],
+ filter = none :: diameter:peer_filter(),
extra = [] :: list(),
- timeout = ?DEFAULT_TIMEOUT :: 0..16#FFFFFFFF,
+ timeout = 5000 :: 0..16#FFFFFFFF, %% for outgoing requests
detach = false :: boolean()}).
-%% Term passed back to receive_message/6 with every incoming message.
+%% Term passed back to receive_message/5 with every incoming message.
-record(recvdata,
{peerT :: ets:tid(),
service_name :: diameter:service_name(),
apps :: [#diameter_app{}],
sequence :: diameter:sequence(),
- codec :: [{string_decode, boolean()}
- | {strict_mbit, boolean()}
- | {incoming_maxlen, diameter:message_length()}]}).
+ counters :: boolean(),
+ codec :: #{decode_format := diameter:decode_format(),
+ avp_dictionaries => nonempty_list(module()),
+ string_decode := boolean(),
+ strict_arities => diameter:strict_arities(),
+ strict_mbit := boolean(),
+ incoming_maxlen := diameter:message_length()}}).
%% Note that incoming_maxlen is currently handled in diameter_peer_fsm,
%% so that any message exceeding the maximum is discarded. Retain the
%% option in case we want to extend the values and semantics.
@@ -88,24 +92,30 @@
{ref :: reference(), %% used to receive answer
caller :: pid() | undefined, %% calling process
handler :: pid(), %% request process
- transport :: pid() | undefined, %% peer process
- caps :: #diameter_caps{} | undefined, %% of connection
+ peer :: undefined | {pid(), #diameter_caps{}},
+ caps :: undefined, %% no longer used
packet :: #diameter_packet{} | undefined}). %% of request
%% ---------------------------------------------------------------------------
-%% # make_recvdata/1
+%% make_recvdata/1
%% ---------------------------------------------------------------------------
make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) ->
- {_,_} = Mask = proplists:get_value(sequence, SvcOpts),
- #recvdata{service_name = SvcName,
- peerT = PeerT,
- apps = Apps,
- sequence = Mask,
- codec = [T || {K,_} = T <- SvcOpts,
- lists:member(K, [string_decode,
- incoming_maxlen,
- strict_mbit])]}.
+ #{sequence := {_,_} = Mask, spawn_opt := Opts, traffic_counters := B}
+ = SvcOpts,
+ {Opts, #recvdata{service_name = SvcName,
+ peerT = PeerT,
+ apps = Apps,
+ sequence = Mask,
+ counters = B,
+ codec = maps:with([decode_format,
+ avp_dictionaries,
+ string_decode,
+ strict_arities,
+ strict_mbit,
+ ordered_encode,
+ incoming_maxlen],
+ SvcOpts)}}.
%% ---------------------------------------------------------------------------
%% peer_up/1
@@ -181,7 +191,7 @@ incr_error(Dir, Id, TPid) ->
%% ---------------------------------------------------------------------------
-spec incr_rc(send|recv, Pkt, TPid, DictT)
- -> {Counter, non_neg_integer()}
+ -> Counter
| Reason
when Pkt :: #diameter_packet{},
TPid :: pid(),
@@ -192,56 +202,62 @@ incr_error(Dir, Id, TPid) ->
| {'Experimental-Result', integer(), integer()},
Reason :: atom().
-incr_rc(Dir, Pkt, TPid, {_, AppDict, _} = DictT) ->
- try
- incr_result(Dir, Pkt, TPid, DictT)
+incr_rc(Dir, Pkt, TPid, {MsgDict, AppDict, Dict0}) ->
+ incr_rc(Dir, Pkt, TPid, MsgDict, AppDict, Dict0);
+
+incr_rc(Dir, Pkt, TPid, Dict0) ->
+ incr_rc(Dir, Pkt, TPid, Dict0, Dict0, Dict0).
+
+%% incr_rc/6
+
+incr_rc(Dir, Pkt, TPid, MsgDict, AppDict, Dict0) ->
+ try get_result(Dir, MsgDict, Dict0, Pkt) of
+ false ->
+ unknown;
+ Avp ->
+ incr_result(Dir, Avp, Pkt, TPid, AppDict)
catch
exit: {E,_} when E == no_result_code;
E == invalid_error_bit ->
incr(TPid, {msg_id(Pkt#diameter_packet.header, AppDict), Dir, E}),
E
- end;
-
-incr_rc(Dir, Pkt, TPid, Dict0) ->
- incr_rc(Dir, Pkt, TPid, {Dict0, Dict0, Dict0}).
+ end.
%% ---------------------------------------------------------------------------
-%% # receive_message/6
+%% receive_message/5
%%
-%% Handle an incoming Diameter message.
+%% Handle an incoming Diameter message in a watchdog process.
%% ---------------------------------------------------------------------------
-%% Handle an incoming Diameter message in the watchdog process.
-
-receive_message(TPid, Route, Pkt, false, Dict0, RecvData) ->
- incoming(TPid, Route, Pkt, Dict0, RecvData);
-
-receive_message(TPid, Route, Pkt, NPid, Dict0, RecvData) ->
- NPid ! {diameter, incoming(TPid, Route, Pkt, Dict0, RecvData)}.
-
-%% incoming/4
+-spec receive_message(pid(), Route, #diameter_packet{}, module(), RecvData)
+ -> pid() %% request handler
+ | boolean() %% answer, known request or not
+ | discard %% request discarded by MFA
+ when Route :: {Handler, RequestRef, TPid}
+ | Ack,
+ RecvData :: {[SpawnOpt], #recvdata{}},
+ SpawnOpt :: term(),
+ Handler :: pid(),
+ RequestRef :: reference(),
+ TPid :: pid(),
+ Ack :: boolean().
-incoming(TPid, Route, Pkt, Dict0, RecvData)
- when is_pid(TPid) ->
+receive_message(TPid, Route, Pkt, Dict0, RecvData) ->
#diameter_packet{header = #diameter_header{is_request = R}} = Pkt,
recv(R, Route, TPid, Pkt, Dict0, RecvData).
%% recv/6
%% Incoming request ...
-recv(true, false, TPid, Pkt, Dict0, T) ->
- try
- {request, spawn_request(TPid, Pkt, Dict0, T)}
- catch
- error: system_limit = E -> %% discard
- ?LOG(error, E),
- discard
- end;
+recv(true, Ack, TPid, Pkt, Dict0, T)
+ when is_boolean(Ack) ->
+ {Opts, RecvData} = T,
+ spawn_request(Ack, TPid, Pkt, Dict0, RecvData, Opts);
%% ... answer to known request ...
recv(false, {Pid, Ref, TPid}, _, Pkt, Dict0, _) ->
Pid ! {answer, Ref, TPid, Dict0, Pkt},
- {answer, Pid};
+ true;
%% Note that failover could have happened prior to this message being
%% received and triggering failback. That is, both a failover message
@@ -256,77 +272,100 @@ recv(false, {Pid, Ref, TPid}, _, Pkt, Dict0, _) ->
recv(false, false, TPid, Pkt, _, _) ->
?LOG(discarded, Pkt#diameter_packet.header),
incr(TPid, {{unknown, 0}, recv, discarded}),
- discard.
+ false.
+
+%% spawn_request/6
+
+%% An MFA should return a pid() or the atom 'discard'. The latter
+%% results in an acknowledgment back to the transport process when
+%% appropriate, to ensure that send/recv callbacks can count
+%% outstanding requests. Acknowledgement is implicit if the
+%% handler process dies (in a handle_request callback for example).
+spawn_request(Ack, TPid, Pkt, Dict0, RecvData, {M,F,A}) ->
+ ReqF = fun() ->
+ ack(Ack, TPid, recv_request(Ack, TPid, Pkt, Dict0, RecvData))
+ end,
+ ack(Ack, TPid, apply(M, F, [ReqF | A]));
-%% spawn_request/4
+%% A spawned process acks implicitly when it dies, so there's no need
+%% to handle 'discard'.
+spawn_request(Ack, TPid, Pkt, Dict0, RecvData, Opts) ->
+ spawn_opt(fun() ->
+ recv_request(Ack, TPid, Pkt, Dict0, RecvData)
+ end,
+ Opts).
-spawn_request(TPid, Pkt, Dict0, {Opts, RecvData}) ->
- spawn_request(TPid, Pkt, Dict0, Opts, RecvData);
-spawn_request(TPid, Pkt, Dict0, RecvData) ->
- spawn_request(TPid, Pkt, Dict0, ?DEFAULT_SPAWN_OPTS, RecvData).
+%% ack/3
-spawn_request(TPid, Pkt, Dict0, Opts, RecvData) ->
- spawn_opt(fun() -> recv_request(TPid, Pkt, Dict0, RecvData) end, Opts).
+ack(Ack, TPid, RC) ->
+ RC == discard andalso Ack andalso (TPid ! {send, false}),
+ RC.
%% ---------------------------------------------------------------------------
-%% recv_request/4
+%% recv_request/5
%% ---------------------------------------------------------------------------
-recv_request(TPid,
+-spec recv_request(Ack :: boolean(),
+ TPid :: pid(),
+ #diameter_packet{},
+ Dict0 :: module(),
+ #recvdata{})
+ -> ok %% answer was sent
+ | discard %% or not
+ | false. %% no transport
+
+recv_request(Ack,
+ TPid,
#diameter_packet{header = #diameter_header{application_id = Id}}
= Pkt,
Dict0,
#recvdata{peerT = PeerT,
apps = Apps,
- codec = Opts}
+ counters = Count}
= RecvData) ->
- diameter_codec:setopts([{common_dictionary, Dict0} | Opts]),
- send_A(recv_R(diameter_service:find_incoming_app(PeerT, TPid, Id, Apps),
- TPid,
- Pkt,
- Dict0,
- RecvData),
- TPid,
- Dict0,
- RecvData).
-
-%% recv_R/5
-
-recv_R({#diameter_app{id = Id, dictionary = AppDict} = App, Caps},
- TPid,
- Pkt0,
- Dict0,
- RecvData) ->
- incr(recv, Pkt0, TPid, AppDict),
- Pkt = errors(Id, diameter_codec:decode(Id, AppDict, Pkt0)),
- incr_error(recv, Pkt, TPid, AppDict),
- {Caps, Pkt, App, recv_R(App, TPid, Dict0, Caps, RecvData, Pkt)};
-%% Note that the decode is different depending on whether or not Id is
-%% ?APP_ID_RELAY.
-
-%% DIAMETER_APPLICATION_UNSUPPORTED 3007
-%% A request was sent for an application that is not supported.
-
-recv_R(#diameter_caps{}
- = Caps,
- _TPid,
- #diameter_packet{errors = Es}
- = Pkt,
- _Dict0,
- _RecvData) ->
- {Caps, Pkt#diameter_packet{avps = collect_avps(Pkt),
- errors = [3007 | Es]}};
+ Ack andalso (TPid ! {handler, self()}),
+ case diameter_service:find_incoming_app(PeerT, TPid, Id, Apps) of
+ {#diameter_app{id = Aid, dictionary = AppDict} = App, Caps} ->
+ Count andalso incr(recv, Pkt, TPid, AppDict),
+ DecPkt = decode(Aid, AppDict, RecvData, Pkt),
+ Count andalso incr_error(recv, DecPkt, TPid, AppDict),
+ send_A(recv_R(App, TPid, Dict0, Caps, RecvData, DecPkt),
+ TPid,
+ App,
+ Dict0,
+ RecvData,
+ DecPkt,
+ Caps);
+ #diameter_caps{} = Caps ->
+ %% DIAMETER_APPLICATION_UNSUPPORTED 3007
+ %% A request was sent for an application that is not
+ %% supported.
+ RC = 3007,
+ DecPkt = diameter_codec:collect_avps(Pkt),
+ send_answer(answer_message(RC, Dict0, Caps, DecPkt),
+ TPid,
+ Dict0,
+ Dict0,
+ Dict0,
+ RecvData,
+ DecPkt,
+ [[]]);
+ false = No -> %% transport has gone down
+ No
+ end.
-recv_R(false = No, _, _, _, _) -> %% transport has gone down
- No.
+%% decode/4
-collect_avps(Pkt) ->
- case diameter_codec:collect_avps(Pkt) of
- {_Error, Avps} ->
- Avps;
- Avps ->
- Avps
- end.
+decode(Id, Dict, #recvdata{codec = Opts}, Pkt) ->
+ errors(Id, diameter_codec:decode(Id, Dict, Opts, Pkt)).
+
+%% send_A/7
+
+send_A([T | Fs], TPid, App, Dict0, RecvData, DecPkt, Caps) ->
+ send_A(T, TPid, App, Dict0, RecvData, DecPkt, Caps, Fs);
+
+send_A(discard = No, _, _, _, _, _, _) ->
+ No.
%% recv_R/6
@@ -337,9 +376,9 @@ recv_R(#diameter_app{options = [_, {request_errors, E} | _]},
_Caps,
_RecvData,
#diameter_packet{errors = [RC|_]}) %% a detected 3xxx is hd
- when E == answer, (Dict0 /= ?BASE orelse 3 == RC div 1000);
+ when E == answer, Dict0 /= ?BASE orelse 3 == RC div 1000;
E == answer_3xxx, 3 == RC div 1000 ->
- {{answer_message, rc(RC)}, [], []};
+ [{answer_message, rc(RC)}, []];
%% ... or make a handle_request callback. Note that
%% Pkt#diameter_packet.msg = undefined in the 3001 case.
@@ -431,24 +470,24 @@ errors(_, Pkt) ->
%% command code in this case. It will also then ignore Dict and use
%% the base encoder.
request_cb({reply, _Ans} = T, _App, EvalPktFs, EvalFs) ->
- {T, EvalPktFs, EvalFs};
+ [T, EvalPktFs | EvalFs];
%% An 3xxx result code, for which the E-bit is set in the header.
request_cb({protocol_error, RC}, _App, EvalPktFs, EvalFs)
when 3 == RC div 1000 ->
- {{answer_message, RC}, EvalPktFs, EvalFs};
+ [{answer_message, RC}, EvalPktFs | EvalFs];
request_cb({answer_message, RC} = T, _App, EvalPktFs, EvalFs)
when 3 == RC div 1000;
5 == RC div 1000 ->
- {T, EvalPktFs, EvalFs};
+ [T, EvalPktFs | EvalFs];
%% RFC 3588 says we must reply 3001 to anything unrecognized or
%% unsupported. 'noreply' is undocumented (and inappropriately named)
%% backwards compatibility for this, protocol_error the documented
%% alternative.
request_cb(noreply, _App, EvalPktFs, EvalFs) ->
- {{answer_message, 3001}, EvalPktFs, EvalFs};
+ [{answer_message, 3001}, EvalPktFs | EvalFs];
%% Relay a request to another peer. This is equivalent to doing an
%% explicit call/4 with the message in question except that (1) a loop
@@ -469,7 +508,7 @@ request_cb({A, Opts}, #diameter_app{id = Id}, EvalPktFs, EvalFs)
when A == relay, Id == ?APP_ID_RELAY;
A == proxy, Id /= ?APP_ID_RELAY;
A == resend ->
- {{call, Opts}, EvalPktFs, EvalFs};
+ [{call, Opts}, EvalPktFs | EvalFs];
request_cb(discard = No, _, _, _) ->
No;
@@ -483,71 +522,104 @@ request_cb({eval, RC, F}, App, EvalPktFs, Fs) ->
request_cb(T, App, _, _) ->
?ERROR({invalid_return, T, handle_request, App}).
-%% send_A/4
-
-send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application
- #diameter_packet{errors = [RC|_]} = Pkt,
- send_A(answer_message(RC, Caps, Dict0, Pkt),
- TPid,
- {Dict0, Dict0},
- Pkt,
- [],
- []);
-
-send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) ->
- send_A(answer(T, Caps, Pkt, App, Dict0, RecvData),
- TPid,
- {App#diameter_app.dictionary, Dict0},
- Pkt,
- EvalPktFs,
- EvalFs);
-
-send_A(_, _, _, _) ->
- ok.
-
-%% send_A/6
-
-send_A(T, TPid, {AppDict, Dict0} = DictT0, ReqPkt, EvalPktFs, EvalFs) ->
- {MsgDict, Pkt} = reply(T, TPid, DictT0, EvalPktFs, ReqPkt),
- incr(send, Pkt, TPid, AppDict),
- incr_rc(send, Pkt, TPid, {MsgDict, AppDict, Dict0}), %% count outgoing
- send(TPid, Pkt),
- lists:foreach(fun diameter_lib:eval/1, EvalFs).
-
-%% answer/6
+%% send_A/8
-answer({reply, Ans}, _Caps, _Pkt, App, Dict0, _RecvData) ->
- {msg_dict(App#diameter_app.dictionary, Dict0, Ans), Ans};
-
-answer({call, Opts}, Caps, Pkt, App, Dict0, RecvData) ->
- #diameter_caps{origin_host = {OH,_}}
- = Caps,
- #diameter_packet{avps = Avps}
- = Pkt,
- {Code, _Flags, Vid} = Dict0:avp_header('Route-Record'),
- resend(is_loop(Code, Vid, OH, Dict0, Avps),
- Opts,
- Caps,
- Pkt,
- App,
- Dict0,
- RecvData);
+send_A({reply, Ans}, TPid, App, Dict0, RecvData, Pkt, _Caps, Fs) ->
+ AppDict = App#diameter_app.dictionary,
+ MsgDict = msg_dict(AppDict, Dict0, Ans),
+ send_answer(Ans,
+ TPid,
+ MsgDict,
+ AppDict,
+ Dict0,
+ RecvData,
+ Pkt,
+ Fs);
+
+send_A({call, Opts}, TPid, App, Dict0, RecvData, Pkt, Caps, Fs) ->
+ AppDict = App#diameter_app.dictionary,
+ case resend(Opts, Caps, Pkt, App, Dict0, RecvData) of
+ #diameter_packet{bin = Bin} = Ans -> %% answer: reset hop by hop id
+ #diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
+ transport_data = TD}
+ = Pkt,
+ Reset = diameter_codec:hop_by_hop_id(Id, Bin),
+ MsgDict = msg_dict(AppDict, Dict0, Ans),
+ send_answer(Ans#diameter_packet{bin = Reset,
+ transport_data = TD},
+ TPid,
+ MsgDict,
+ AppDict,
+ Dict0,
+ RecvData#recvdata.counters,
+ Fs);
+ RC ->
+ send_answer(answer_message(RC, Dict0, Caps, Pkt),
+ TPid,
+ Dict0,
+ AppDict,
+ Dict0,
+ RecvData,
+ Pkt,
+ Fs)
+ end;
%% RFC 3588 only allows 3xxx errors in an answer-message. RFC 6733
%% added the possibility of setting 5xxx.
-answer({answer_message, RC} = T, Caps, Pkt, App, Dict0, _RecvData) ->
+
+send_A({answer_message, RC} = T, TPid, App, Dict0, RecvData, Pkt, Caps, Fs) ->
Dict0 /= ?BASE orelse 3 == RC div 1000
orelse ?ERROR({invalid_return, T, handle_request, App}),
- answer_message(RC, Caps, Dict0, Pkt).
+ send_answer(answer_message(RC, Dict0, Caps, Pkt),
+ TPid,
+ Dict0,
+ App#diameter_app.dictionary,
+ Dict0,
+ RecvData,
+ Pkt,
+ Fs).
+
+%% send_answer/8
+
+%% Skip the setting of Result-Code and Failed-AVP's below. This is
+%% undocumented and shouldn't be relied on.
+send_answer([Ans], TPid, MsgDict, AppDict, Dict0, RecvData, Pkt, Fs)
+ when [] == Pkt#diameter_packet.errors ->
+ send_answer(Ans, TPid, MsgDict, AppDict, Dict0, RecvData, Pkt, Fs);
+send_answer([Ans], TPid, MsgDict, AppDict, Dict0, RecvData, Pkt0, Fs) ->
+ Pkt = Pkt0#diameter_packet{errors = []},
+ send_answer(Ans, TPid, MsgDict, AppDict, Dict0, RecvData, Pkt, Fs);
+
+send_answer(Ans, TPid, MsgDict, AppDict, Dict0, RecvData, DecPkt, Fs) ->
+ Pkt = encode({MsgDict, AppDict},
+ TPid,
+ RecvData#recvdata.codec,
+ make_answer_packet(Ans, DecPkt, MsgDict, Dict0)),
+ send_answer(Pkt,
+ TPid,
+ MsgDict,
+ AppDict,
+ Dict0,
+ RecvData#recvdata.counters,
+ Fs).
+
+%% send_answer/7
+
+send_answer(Pkt, TPid, MsgDict, AppDict, Dict0, Count, [EvalPktFs | EvalFs]) ->
+ eval_packet(Pkt, EvalPktFs),
+ Count andalso begin
+ incr(send, Pkt, TPid, AppDict),
+ incr_rc(send, Pkt, TPid, MsgDict, AppDict, Dict0)
+ end,
+ send(TPid, z(Pkt), _Route = self()),
+ lists:foreach(fun diameter_lib:eval/1, EvalFs).
%% msg_dict/3
%%
%% Return the dictionary defining the message grammar in question: the
%% application dictionary or the common dictionary.
-msg_dict(AppDict, Dict0, [Msg])
- when is_list(Msg);
- is_tuple(Msg) ->
+msg_dict(AppDict, Dict0, [Msg]) ->
msg_dict(AppDict, Dict0, Msg);
msg_dict(AppDict, Dict0, Msg) ->
@@ -566,7 +638,7 @@ is_answer_message(#diameter_packet{msg = Msg}, Dict0) ->
is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) ->
E andalso not R;
-%% Message sent as a tagged avp/value list.
+%% Message sent as a map or tagged avp/value list.
is_answer_message([Name | _], _) ->
Name == 'answer-message';
@@ -578,14 +650,10 @@ is_answer_message(Rec, Dict) ->
error:_ -> false
end.
-%% answer_message/4
+%% resend/6
-answer_message(RC,
- #diameter_caps{origin_host = {OH,_},
- origin_realm = {OR,_}},
- Dict0,
- Pkt) ->
- {Dict0, answer_message(OH, OR, RC, Dict0, Pkt)}.
+resend(Opts, Caps, Pkt, App, Dict0, RecvData) ->
+ resend(is_loop(Dict0, Caps, Pkt), Opts, Caps, Pkt, App, Dict0, RecvData).
%% resend/7
@@ -595,8 +663,8 @@ answer_message(RC,
%% if one is available, but the peer reporting the error has
%% identified a configuration problem.
-resend(true, _Opts, Caps, Pkt, _App, Dict0, _RecvData) ->
- answer_message(3005, Caps, Dict0, Pkt);
+resend(true, _Opts, _Caps, _Pkt, _App, _Dict0, _RecvData) ->
+ 3005;
%% 6.1.8. Relaying and Proxying Requests
%%
@@ -606,11 +674,9 @@ resend(true, _Opts, Caps, Pkt, _App, Dict0, _RecvData) ->
resend(false,
Opts,
- #diameter_caps{origin_host = {_,OH}}
- = Caps,
+ #diameter_caps{origin_host = {_,OH}},
#diameter_packet{header = Hdr0,
- avps = Avps}
- = Pkt,
+ avps = Avps},
App,
Dict0,
#recvdata{service_name = SvcName,
@@ -618,8 +684,13 @@ resend(false,
Route = #diameter_avp{data = {Dict0, 'Route-Record', OH}},
Seq = diameter_session:sequence(Mask),
Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
- Msg = [Hdr, Route | Avps], %% reordered at encode
- resend(send_request(SvcName, App, Msg, Opts), Caps, Dict0, Pkt).
+ Msg = [Hdr | Avps ++ [Route]],
+ case send_request(SvcName, App, Msg, Opts) of
+ #diameter_packet{} = Ans ->
+ Ans;
+ _ ->
+ 3002 %% DIAMETER_UNABLE_TO_DELIVER.
+ end.
%% The incoming request is relayed with the addition of a
%% Route-Record. Note the requirement on the return from call/4 below,
%% which places a requirement on the value returned by the
@@ -636,96 +707,38 @@ resend(false,
%% RFC 6.3 says that a relay agent does not modify Origin-Host but
%% says nothing about a proxy. Assume it should behave the same way.
-%% resend/4
-%%
-%% Relay a reply to a relayed request.
-
-%% Answer from the peer: reset the hop by hop identifier.
-resend(#diameter_packet{bin = B}
- = Pkt,
- _Caps,
- _Dict0,
- #diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
- transport_data = TD}) ->
- Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
- transport_data = TD};
-%% TODO: counters
+%% is_loop/3
-%% Or not: DIAMETER_UNABLE_TO_DELIVER.
-resend(_, Caps, Dict0, Pkt) ->
- answer_message(3002, Caps, Dict0, Pkt).
+is_loop(Dict0,
+ #diameter_caps{origin_host = {OH,_}},
+ #diameter_packet{avps = Avps}) ->
+ {Code, _Flags, Vid} = Dict0:avp_header('Route-Record'),
+ is_loop(Code, Vid, OH, Avps).
-%% is_loop/5
+%% is_loop/4
%%
%% Is there a Route-Record AVP with our Origin-Host?
-is_loop(Code,
- Vid,
- Bin,
- _Dict0,
- [#diameter_avp{code = Code, vendor_id = Vid, data = Bin} | _]) ->
+is_loop(Code, Vid, Bin, [#diameter_avp{code = Code,
+ vendor_id = Vid,
+ data = Bin}
+ | _]) ->
true;
-is_loop(_, _, _, _, []) ->
+is_loop(_, _, _, []) ->
false;
-is_loop(Code, Vid, OH, Dict0, [_ | Avps])
+is_loop(Code, Vid, OH, [_ | Avps])
when is_binary(OH) ->
- is_loop(Code, Vid, OH, Dict0, Avps);
-
-is_loop(Code, Vid, OH, Dict0, Avps) ->
- is_loop(Code, Vid, Dict0:avp(encode, OH, 'Route-Record'), Dict0, Avps).
-
-%% reply/5
+ is_loop(Code, Vid, OH, Avps);
-%% Local answer ...
-reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
- local(Ans, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt);
-
-%% ... or relayed.
-reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, Fs, _ReqPkt) ->
- eval_packet(Pkt, Fs),
- {msg_dict(AppDict, Dict0, Pkt), Pkt}.
-
-%% local/5
-%%
-%% Send a locally originating reply.
-
-%% Skip the setting of Result-Code and Failed-AVP's below. This is
-%% undocumented and shouldn't be relied on.
-local([Msg], TPid, DictT, Fs, ReqPkt)
- when is_list(Msg);
- is_tuple(Msg) ->
- local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []});
-
-local(Msg, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt) ->
- Pkt = encode({MsgDict, AppDict},
- TPid,
- reset(make_answer_packet(Msg, ReqPkt), MsgDict, Dict0),
- Fs),
- {MsgDict, Pkt}.
-
-%% reset/3
-
-%% Header/avps list: send as is.
-reset(#diameter_packet{msg = [#diameter_header{} | _]} = Pkt, _, _) ->
- Pkt;
-
-%% No errors to set or errors explicitly ignored.
-reset(#diameter_packet{errors = Es} = Pkt, _, _)
- when Es == [];
- Es == false ->
- Pkt;
-
-%% Otherwise possibly set Result-Code and/or Failed-AVP.
-reset(#diameter_packet{msg = Msg, errors = Es} = Pkt, Dict, Dict0) ->
- {RC, Failed} = select_error(Msg, Es, Dict0),
- Pkt#diameter_packet{msg = reset(Msg, Dict, RC, Failed)}.
+is_loop(Code, Vid, OH, Avps) ->
+ is_loop(Code, Vid, list_to_binary(OH), Avps).
%% select_error/3
%%
%% Extract the first appropriate RC or {RC, #diameter_avp{}}
-%% pair from an errors list, and accumulate all #diameter_avp{}.
+%% pair from an errors list, along with any leading #diameter_avp{}.
%%
%% RFC 6733:
%%
@@ -740,97 +753,143 @@ reset(#diameter_packet{msg = Msg, errors = Es} = Pkt, Dict, Dict0) ->
%% indicated by the Result-Code AVP. For practical purposes, this
%% Failed-AVP would typically refer to the first AVP processing error
%% that a Diameter node encounters.
+%%
+%% 3xxx can only be set in an answer setting the E-bit. RFC 6733 also
+%% allows 5xxx, RFC 3588 doesn't.
-select_error(Msg, Es, Dict0) ->
- {RC, Avps} = lists:foldl(fun(T,A) -> select(T, A, Dict0) end,
- {is_answer_message(Msg, Dict0), []},
- Es),
- {RC, lists:reverse(Avps)}.
+select_error(E, Es, Dict0) ->
+ select(E, Es, Dict0, []).
-%% Only integer() and {integer(), #diameter_avp{}} are the result of
-%% decode. #diameter_avp{} can only be set in a reply for encode.
+%% select/4
-select(#diameter_avp{} = A, {RC, As}, _) ->
- {RC, [A|As]};
+select(E, [{RC, _} = T | Es], Dict0, Avps) ->
+ select(E, RC, T, Es, Dict0, Avps);
-select(_, {RC, _} = Acc, _)
- when is_integer(RC) ->
- Acc;
+select(E, [#diameter_avp{} = A | Es], Dict0, Avps) ->
+ select(E, Es, Dict0, [A | Avps]);
-select({RC, #diameter_avp{} = A}, {IsAns, As} = Acc, Dict0)
- when is_integer(RC) ->
- case is_result(RC, IsAns, Dict0) of
- true -> {RC, [A|As]};
- false -> Acc
- end;
+select(E, [RC | Es], Dict0, Avps) ->
+ select(E, RC, RC, Es, Dict0, Avps);
-select(RC, {IsAns, As} = Acc, Dict0)
- when is_boolean(IsAns), is_integer(RC) ->
- case is_result(RC, IsAns, Dict0) of
- true -> {RC, As};
- false -> Acc
- end.
+select(_, [], _, Avps) ->
+ Avps.
-%% reset/4
+%% select/6
+
+select(E, RC, T, _, Dict0, Avps)
+ when E, 3000 =< RC, RC < 4000; %% E-bit with 3xxx
+ E, ?BASE /= Dict0, 5000 =< RC, RC < 6000; %% E-bit with 5xxx
+ not E, RC < 3000 orelse 4000 =< RC -> %% no E-bit
+ [T | Avps];
-reset(Msg, Dict, RC, Avps) ->
- FailedAVP = failed_avp(Msg, Avps, Dict),
- ResultCode = rc(Msg, RC, Dict),
- set(set(Msg, FailedAVP, Dict), ResultCode, Dict).
+select(E, _, _, Es, Dict0, Avps) ->
+ select(E, Es, Dict0, Avps).
%% eval_packet/2
eval_packet(Pkt, Fs) ->
lists:foreach(fun(F) -> diameter_lib:eval([F,Pkt]) end, Fs).
-%% make_answer_packet/2
+%% make_answer_packet/4
%% Use decode errors to set Result-Code and/or Failed-AVP unless the
%% the errors field has been explicitly set. Unfortunately, the
%% default value is the empty list rather than 'undefined' so use the
%% atom 'false' for "set nothing". (This is historical and changing
-%% the default value would require modules including diameter.hrl to
-%% be recompiled.)
-make_answer_packet(#diameter_packet{errors = []}
- = Pkt,
- #diameter_packet{errors = [_|_] = Es}
- = ReqPkt) ->
- make_answer_packet(Pkt#diameter_packet{errors = Es}, ReqPkt);
+%% the default value would impact anyone expecting relying on the old
+%% default.)
-%% A reply message clears the R and T flags and retains the P flag.
-%% The E flag will be set at encode. 6.2 of 3588 requires the same P
-%% flag on an answer as on the request. A #diameter_packet{} returned
-%% from a handle_request callback can circumvent this by setting its
-%% own header values.
make_answer_packet(#diameter_packet{header = Hdr,
msg = Msg,
errors = Es,
transport_data = TD},
- #diameter_packet{header = ReqHdr}) ->
- Hdr0 = ReqHdr#diameter_header{version = ?DIAMETER_VERSION,
- is_request = false,
- is_error = undefined,
- is_retransmitted = false},
- #diameter_packet{header = fold_record(Hdr0, Hdr),
- msg = Msg,
- errors = Es,
+ #diameter_packet{header = Hdr0,
+ errors = Es0},
+ MsgDict,
+ Dict0) ->
+ #diameter_packet{header = make_answer_header(Hdr0, Hdr),
+ msg = reset(Msg, Es0, Es, MsgDict, Dict0),
transport_data = TD};
%% Binaries and header/avp lists are sent as-is.
-make_answer_packet(Bin, #diameter_packet{transport_data = TD})
+make_answer_packet(Bin, #diameter_packet{transport_data = TD}, _, _)
when is_binary(Bin) ->
#diameter_packet{bin = Bin,
transport_data = TD};
make_answer_packet([#diameter_header{} | _] = Msg,
- #diameter_packet{transport_data = TD}) ->
+ #diameter_packet{transport_data = TD},
+ _,
+ _) ->
#diameter_packet{msg = Msg,
transport_data = TD};
-%% Otherwise, preserve transport_data.
-make_answer_packet(Msg, #diameter_packet{transport_data = TD} = Pkt) ->
- make_answer_packet(#diameter_packet{msg = Msg, transport_data = TD}, Pkt).
+make_answer_packet(Msg,
+ #diameter_packet{header = Hdr,
+ errors = Es,
+ transport_data = TD},
+ MsgDict,
+ Dict0) ->
+ #diameter_packet{header = make_answer_header(Hdr, undefined),
+ msg = reset(Msg, [], Es, MsgDict, Dict0),
+ transport_data = TD}.
+
+%% make_answer_header/2
+
+%% A reply message clears the R and T flags and retains the P flag.
+%% The E flag will be set at encode. 6.2 of 3588 requires the same P
+%% flag on an answer as on the request. A #diameter_packet{} returned
+%% from a handle_request callback can circumvent this by setting its
+%% own header values.
+make_answer_header(ReqHdr, Hdr) ->
+ Hdr0 = ReqHdr#diameter_header{version = ?DIAMETER_VERSION,
+ is_request = false,
+ is_error = undefined,
+ is_retransmitted = false},
+ fold_record(Hdr0, Hdr).
+
+%% reset/5
+
+reset(Msg, [_|_] = Es0, [] = Es, MsgDict, Dict0) ->
+ reset(Msg, Es, Es0, MsgDict, Dict0);
+
+reset(Msg, _, Es, _, _)
+ when Es == false;
+ Es == [] ->
+ Msg;
+
+reset(Msg, _, Es, MsgDict, Dict0) ->
+ E = is_answer_message(Msg, Dict0),
+ reset(Msg, select_error(E, Es, Dict0), choose(E, Dict0, MsgDict)).
+
+%% reset/4
+%%
+%% Set Result-Code and/or Failed-AVP (maybe). Only RC and {RC, AVP}
+%% are the result of decode. AVP or {RC, [AVP]} can be set in an
+%% answer for encode, as a convenience for injecting additional AVPs
+%% into Failed-AVP; eg. 5001 = DIAMETER_AVP_UNSUPPORTED.
+
+reset(Msg, [], _) ->
+ Msg;
+
+reset(Msg, [{RC, As} | Avps], Dict)
+ when is_list(As) ->
+ reset(Msg, [RC | As ++ Avps], Dict);
+
+reset(Msg, [{RC, Avp} | Avps], Dict) ->
+ reset(Msg, [RC, Avp | Avps], Dict);
+
+reset(Msg, [#diameter_avp{} | _] = Avps, Dict) ->
+ set(Msg, failed_avp(Msg, Avps, Dict), Dict);
-%% Reply as name and tuple list ...
+reset(Msg, [RC | Avps], Dict) ->
+ set(Msg, rc(Msg, RC, Dict) ++ failed_avp(Msg, Avps, Dict), Dict).
+
+%% set/3
+
+%% Reply as name/values list ...
+set([Name|As], Avps, _)
+ when is_map(As) ->
+ [Name | maps:merge(As, maps:from_list(Avps))];
set([_|_] = Ans, Avps, _) ->
Ans ++ Avps; %% Values nearer tail take precedence.
@@ -842,11 +901,7 @@ set(Rec, Avps, Dict) ->
%%
%% Turn the result code into a list if its optional and only set it if
%% the arity is 1 or {0,1}. In other cases (which probably shouldn't
-%% exist in practise) we can't know what's appropriate.
-
-rc(_, B, _)
- when is_boolean(B) ->
- [];
+%% exist in practice) we can't know what's appropriate.
rc([MsgName | _], RC, Dict) ->
K = 'Result-Code',
@@ -864,36 +919,47 @@ rc(Rec, RC, Dict) ->
failed_avp(_, [] = No, _) ->
No;
-failed_avp(Rec, Avps, Dict) ->
- [failed(Rec, [{'AVP', Avps}], Dict)].
+failed_avp(Msg, [_|_] = Avps, Dict) ->
+ [failed(Msg, [{'AVP', Avps}], Dict)].
+
+%% failed/3
-%% Reply as name and tuple list ...
-failed([MsgName | Values], FailedAvp, Dict) ->
- RecName = Dict:msg2rec(MsgName),
+failed(Msg, FailedAvp, Dict) ->
+ RecName = msg2rec(Msg, Dict),
try
- Dict:'#info-'(RecName, {index, 'Failed-AVP'}),
+ Dict:'#info-'(RecName, {index, 'Failed-AVP'}), %% assert existence
{'Failed-AVP', [FailedAvp]}
catch
error: _ ->
- Avps = proplists:get_value('AVP', Values, []),
+ Avps = values(Msg, 'AVP', Dict),
A = #diameter_avp{name = 'Failed-AVP',
value = FailedAvp},
{'AVP', [A|Avps]}
+ end.
+
+%% msg2rec/2
+
+%% Message as name/values list ...
+msg2rec([MsgName | _], Dict) ->
+ Dict:msg2rec(MsgName);
+
+%% ... or record.
+msg2rec(Rec, _) ->
+ element(1, Rec).
+
+%% values/2
+
+%% Message as name/values list ...
+values([_ | Avps], F, _) ->
+ if is_map(Avps) ->
+ maps:get(F, Avps, []);
+ is_list(Avps) ->
+ proplists:get_value(F, Avps, [])
end;
%% ... or record.
-failed(Rec, FailedAvp, Dict) ->
- try
- RecName = element(1, Rec),
- Dict:'#info-'(RecName, {index, 'Failed-AVP'}),
- {'Failed-AVP', [FailedAvp]}
- catch
- error: _ ->
- Avps = Dict:'#get-'('AVP', Rec),
- A = #diameter_avp{name = 'Failed-AVP',
- value = FailedAvp},
- {'AVP', [A|Avps]}
- end.
+values(Rec, F, Dict) ->
+ Dict:'#get-'(F, Rec).
%% 3. Diameter Header
%%
@@ -962,22 +1028,26 @@ failed(Rec, FailedAvp, Dict) ->
%% Error-Message AVP is not intended to be useful in real-time, and
%% SHOULD NOT be expected to be parsed by network entities.
-%% answer_message/5
+%% answer_message/4
-answer_message(OH, OR, RC, Dict0, #diameter_packet{avps = Avps,
- errors = Es}) ->
- {Code, _, Vid} = Dict0:avp_header('Session-Id'),
+answer_message(RC,
+ Dict0,
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}},
+ #diameter_packet{avps = Avps,
+ errors = Es}) ->
['answer-message', {'Origin-Host', OH},
{'Origin-Realm', OR},
- {'Result-Code', RC}]
- ++ session_id(Code, Vid, Dict0, Avps)
- ++ failed_avp(RC, Es).
+ {'Result-Code', RC}
+ | session_id(Dict0, Avps)
+ ++ failed_avp(RC, Es)
+ ++ proxy_info(Dict0, Avps)].
-session_id(Code, Vid, Dict0, Avps)
- when is_list(Avps) ->
+session_id(Dict0, Avps) ->
+ {Code, _, Vid} = Dict0:avp_header('Session-Id'),
try
#diameter_avp{data = Bin} = find_avp(Code, Vid, Avps),
- [{'Session-Id', [Dict0:avp(decode, Bin, 'Session-Id')]}]
+ [{'Session-Id', [Bin]}]
catch
error: _ ->
[]
@@ -992,6 +1062,14 @@ failed_avp(RC, [_ | Es]) ->
failed_avp(_, [] = No) ->
No.
+proxy_info(Dict0, Avps) ->
+ {Code, _, Vid} = Dict0:avp_header('Proxy-Info'),
+ [{'AVP', [A#diameter_avp{value = undefined}
+ || [#diameter_avp{code = C, vendor_id = I} = A | _]
+ <- Avps,
+ C == Code,
+ I == Vid]}].
+
%% find_avp/3
%% Grouped ...
@@ -1065,48 +1143,31 @@ find_avp(Code, VId, [_ | Avps]) ->
%% Message sent as a header/avps list.
incr_result(send = Dir,
- #diameter_packet{msg = [#diameter_header{} = H | _]}
- = Pkt,
+ Avp,
+ #diameter_packet{msg = [#diameter_header{} = H | _]},
TPid,
- DictT) ->
- incr_res(Dir, Pkt#diameter_packet{header = H}, TPid, DictT);
-
-%% Outgoing message as binary: don't count. (Sending binaries is only
-%% partially supported.)
-incr_result(send, #diameter_packet{header = undefined = No}, _, _) ->
- No;
+ AppDict) ->
+ incr_result(Dir, Avp, H, [], TPid, AppDict);
%% Incoming or outgoing. Outgoing with encode errors never gets here
%% since encode fails.
-incr_result(Dir, Pkt, TPid, DictT) ->
- incr_res(Dir, Pkt, TPid, DictT).
-
-incr_res(Dir,
- #diameter_packet{header = #diameter_header{is_error = E}
- = Hdr,
- errors = Es}
- = Pkt,
- TPid,
- DictT) ->
- {MsgDict, AppDict, Dict0} = DictT,
+incr_result(Dir, Avp, Pkt, TPid, AppDict) ->
+ #diameter_packet{header = H, errors = Es}
+ = Pkt,
+ incr_result(Dir, Avp, H, Es, TPid, AppDict).
+%% incr_result/6
+
+incr_result(Dir, Avp, Hdr, Es, TPid, AppDict) ->
Id = msg_id(Hdr, AppDict),
%% Could be {relay, 0}, in which case the R-bit is redundant since
%% only answers are being counted. Let it be however, so that the
%% same tuple is in both send/recv and result code counters.
%% Count incoming decode errors.
- recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict),
-
- %% Exit on a missing result code.
- T = rc_counter(MsgDict, Dir, Pkt),
- T == false andalso ?LOGX(no_result_code, {MsgDict, Dir, Hdr}),
- {Ctr, RC, Avp} = T,
-
- %% Or on an inappropriate value.
- is_result(RC, E, Dict0)
- orelse ?LOGX(invalid_error_bit, {MsgDict, Dir, Hdr, Avp}),
+ send == Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict),
+ Ctr = rcc(Avp),
incr(TPid, {Id, Dir, Ctr}),
Ctr.
@@ -1151,7 +1212,50 @@ is_result(RC, true, _) ->
incr(TPid, Counter) ->
diameter_stats:incr(Counter, TPid, 1).
-%% rc_counter/3
+%% rcc/1
+
+rcc(#diameter_avp{name = 'Result-Code' = Name, value = V}) ->
+ {Name, head(V)};
+
+rcc(#diameter_avp{name = 'Experimental-Result', value = V}) ->
+ head(V).
+
+%% head/1
+
+head([V|_]) ->
+ V;
+head(V) ->
+ V.
+
+%% rcv/1
+
+rcv(#diameter_avp{name = N, value = V}) ->
+ rcv(N, head(V)).
+
+%% rcv/2
+
+rcv('Experimental-Result', {_,_,N}) ->
+ N;
+
+rcv('Result-Code', N) ->
+ N.
+
+%% get_result/4
+
+%% Message sent as binary: no checks or counting.
+get_result(_, _, _, #diameter_packet{header = undefined}) ->
+ false;
+
+get_result(Dir, MsgDict, Dict0, Pkt) ->
+ Avp = get_result(MsgDict, msg(Dir, Pkt)),
+ Hdr = Pkt#diameter_packet.header,
+ %% Exit on a missing result code or inappropriate value.
+ Avp == false
+ andalso ?LOGX(no_result_code, {MsgDict, Dir, Hdr}),
+ E = Hdr#diameter_header.is_error,
+ is_result(rcv(Avp), E, Dict0)
+ orelse ?LOGX(invalid_error_bit, {MsgDict, Dir, Hdr, Avp}),
+ Avp.
%% RFC 3588, 7.6:
%%
@@ -1159,55 +1263,36 @@ incr(TPid, Counter) ->
%% applications MUST include either one Result-Code AVP or one
%% Experimental-Result AVP.
-rc_counter(Dict, Dir, #diameter_packet{header = H,
- avps = As,
- msg = Msg})
+%% msg/2
+
+msg(Dir, #diameter_packet{header = H,
+ avps = As,
+ msg = Msg})
when Dir == recv; %% decoded incoming
Msg == undefined -> %% relayed outgoing
- rc_counter(Dict, [H|As]);
+ [H|As];
-rc_counter(Dict, _, #diameter_packet{msg = Msg}) ->
- rc_counter(Dict, Msg).
-
-rc_counter(Dict, Msg) ->
- rcc(get_result(Dict, Msg)).
-
-rcc(#diameter_avp{name = 'Result-Code' = Name, value = N} = A)
- when is_integer(N) ->
- {{Name, N}, N, A};
-
-rcc(#diameter_avp{name = 'Result-Code' = Name, value = [N|_]} = A)
- when is_integer(N) ->
- {{Name, N}, N, A};
-
-rcc(#diameter_avp{name = 'Experimental-Result', value = {_,_,N} = T} = A)
- when is_integer(N) ->
- {T, N, A};
-
-rcc(#diameter_avp{name = 'Experimental-Result', value = [{_,_,N} = T|_]} = A)
- when is_integer(N) ->
- {T, N, A};
-
-rcc(_) ->
- false.
+msg(_, #diameter_packet{msg = Msg}) ->
+ Msg.
%% get_result/2
get_result(Dict, Msg) ->
try
[throw(A) || N <- ['Result-Code', 'Experimental-Result'],
- #diameter_avp{} = A <- [get_avp(Dict, N, Msg)]]
- of
- [] -> false
+ #diameter_avp{} = A <- [get_avp(Dict, N, Msg)],
+ is_integer(catch rcv(A))],
+ false
catch
- #diameter_avp{} = A -> A
+ #diameter_avp{} = A ->
+ A
end.
x(T) ->
exit(T).
%% ---------------------------------------------------------------------------
-%% # send_request/4
+%% send_request/4
%%
%% Handle an outgoing Diameter request.
%% ---------------------------------------------------------------------------
@@ -1260,11 +1345,10 @@ answer_rc(_, _, Sent) ->
%%
%% In the process spawned for the outgoing request.
-send_R(SvcName, AppOrAlias, Msg, Opts, Caller) ->
- case pick_peer(SvcName, AppOrAlias, Msg, Opts) of
- {Transport, Mask, SvcOpts} ->
- diameter_codec:setopts(SvcOpts),
- send_request(Transport, Mask, Msg, Opts, Caller, SvcName);
+send_R(SvcName, AppOrAlias, Msg, CallOpts, Caller) ->
+ case pick_peer(SvcName, AppOrAlias, Msg, CallOpts) of
+ {{_,_} = Transport, SvcOpts} ->
+ send_request(Transport, SvcOpts, Msg, CallOpts, Caller, SvcName);
{error, _} = No ->
No
end.
@@ -1272,31 +1356,45 @@ send_R(SvcName, AppOrAlias, Msg, Opts, Caller) ->
%% make_options/1
make_options(Options) ->
- lists:foldl(fun mo/2, #options{}, Options).
-
-mo({timeout, T}, Rec)
- when is_integer(T), 0 =< T ->
- Rec#options{timeout = T};
-
-mo({filter, F}, #options{filter = none} = Rec) ->
- Rec#options{filter = F};
-mo({filter, F}, #options{filter = {all, Fs}} = Rec) ->
- Rec#options{filter = {all, [F | Fs]}};
-mo({filter, F}, #options{filter = F0} = Rec) ->
- Rec#options{filter = {all, [F0, F]}};
-
-mo({extra, L}, #options{extra = X} = Rec)
+ make_opts(Options, [], false, [], none, 5000).
+
+%% Do our own recursion since this is faster than a lists:foldl/3
+%% setting elements in an #options{} accumulator.
+
+make_opts([], Peers, Detach, Extra, Filter, Tmo) ->
+ #options{peers = lists:reverse(Peers),
+ detach = Detach,
+ extra = Extra,
+ filter = Filter,
+ timeout = Tmo};
+
+make_opts([{timeout, Tmo} | Rest], Peers, Detach, Extra, Filter, _)
+ when is_integer(Tmo), 0 =< Tmo ->
+ make_opts(Rest, Peers, Detach, Extra, Filter, Tmo);
+
+make_opts([{filter, F} | Rest], Peers, Detach, Extra, none, Tmo) ->
+ make_opts(Rest, Peers, Detach, Extra, F, Tmo);
+make_opts([{filter, F} | Rest], Peers, Detach, Extra, {all, Fs}, Tmo) ->
+ make_opts(Rest, Peers, Detach, Extra, {all, [F|Fs]}, Tmo);
+make_opts([{filter, F} | Rest], Peers, Detach, Extra, F0, Tmo) ->
+ make_opts(Rest, Peers, Detach, Extra, {all, [F0, F]}, Tmo);
+
+make_opts([{extra, L} | Rest], Peers, Detach, Extra, Filter, Tmo)
when is_list(L) ->
- Rec#options{extra = X ++ L};
+ make_opts(Rest, Peers, Detach, Extra ++ L, Filter, Tmo);
-mo(detach, Rec) ->
- Rec#options{detach = true};
+make_opts([detach | Rest], Peers, _, Extra, Filter, Tmo) ->
+ make_opts(Rest, Peers, true, Extra, Filter, Tmo);
+
+make_opts([{peer, TPid} | Rest], Peers, Detach, Extra, Filter, Tmo)
+ when is_pid(TPid) ->
+ make_opts(Rest, [TPid | Peers], Detach, Extra, Filter, Tmo);
-mo(T, _) ->
+make_opts([T | _], _, _, _, _, _) ->
?ERROR({invalid_option, T}).
%% ---------------------------------------------------------------------------
-%% # send_request/6
+%% send_request/6
%% ---------------------------------------------------------------------------
%% Send an outgoing request in its dedicated process.
@@ -1309,44 +1407,57 @@ mo(T, _) ->
%% The module field of the #diameter_app{} here includes any extra
%% arguments passed to diameter:call/4.
-send_request({TPid, Caps, App}
+send_request({{TPid, _Caps} = TC, App}
= Transport,
- Mask,
- Msg,
- Opts,
+ #{sequence := Mask, traffic_counters := Count}
+ = SvcOpts,
+ Msg0,
+ CallOpts,
Caller,
SvcName) ->
- Pkt = make_prepare_packet(Mask, Msg),
-
- send_R(cb(App, prepare_request, [Pkt, SvcName, {TPid, Caps}]),
- Pkt,
- Transport,
- Opts,
- Caller,
- SvcName,
- []).
+ Pkt = make_prepare_packet(Mask, Msg0),
+
+ case prepare(cb(App, prepare_request, [Pkt, SvcName, TC]), []) of
+ [Msg | Fs] ->
+ ReqPkt = make_request_packet(Msg, Pkt),
+ EncPkt = encode(App#diameter_app.dictionary,
+ TPid,
+ SvcOpts,
+ ReqPkt),
+ eval_packet(EncPkt, Fs),
+ T = send_R(ReqPkt,
+ EncPkt,
+ Transport,
+ CallOpts,
+ Caller,
+ Count,
+ SvcName),
+ Ans = recv_answer(SvcName, App, CallOpts, T),
+ handle_answer(SvcName, Count, SvcOpts, App, Ans);
+ {discard, Reason} ->
+ {error, Reason};
+ discard ->
+ {error, discarded};
+ {error, Reason} ->
+ ?ERROR({invalid_return, Reason, prepare_request, App})
+ end.
-%% send_R/7
+%% prepare/2
-send_R({send, Msg}, Pkt, Transport, Opts, Caller, SvcName, Fs) ->
- send_R(make_request_packet(Msg, Pkt),
- Transport,
- Opts,
- Caller,
- SvcName,
- Fs);
+prepare({send, Msg}, Fs) ->
+ [Msg | Fs];
-send_R({discard, Reason} , _, _, _, _, _, _) ->
- {error, Reason};
+prepare({eval_packet, RC, F}, Fs) ->
+ prepare(RC, [F|Fs]);
-send_R(discard, _, _, _, _, _, _) ->
- {error, discarded};
+prepare({discard, _Reason} = RC, _) ->
+ RC;
-send_R({eval_packet, RC, F}, Pkt, T, Opts, Caller, SvcName, Fs) ->
- send_R(RC, Pkt, T, Opts, Caller, SvcName, [F|Fs]);
+prepare(discard = RC, _) ->
+ RC;
-send_R(E, _, {_, _, App}, _, _, _, _) ->
- ?ERROR({invalid_return, E, prepare_request, App}).
+prepare(Reason, _) ->
+ {error, Reason}.
%% make_prepare_packet/2
%%
@@ -1366,43 +1477,39 @@ make_prepare_packet(Mask, #diameter_packet{msg = [#diameter_header{} = Hdr
make_prepare_packet(Mask, #diameter_packet{header = Hdr} = Pkt) ->
Pkt#diameter_packet{header = make_prepare_header(Mask, Hdr)};
+make_prepare_packet(Mask, [#diameter_header{} = Hdr | Avps]) ->
+ #diameter_packet{msg = [make_prepare_header(Mask, Hdr) | Avps]};
+
make_prepare_packet(Mask, Msg) ->
- make_prepare_packet(Mask, #diameter_packet{msg = Msg}).
+ #diameter_packet{header = make_prepare_header(Mask, undefined),
+ msg = Msg}.
%% make_prepare_header/2
make_prepare_header(Mask, undefined) ->
Seq = diameter_session:sequence(Mask),
- make_prepare_header(#diameter_header{end_to_end_id = Seq,
- hop_by_hop_id = Seq});
-
-make_prepare_header(Mask, #diameter_header{end_to_end_id = undefined,
- hop_by_hop_id = undefined}
- = H) ->
- Seq = diameter_session:sequence(Mask),
- make_prepare_header(H#diameter_header{end_to_end_id = Seq,
- hop_by_hop_id = Seq});
-
-make_prepare_header(Mask, #diameter_header{end_to_end_id = undefined} = H) ->
- Seq = diameter_session:sequence(Mask),
- make_prepare_header(H#diameter_header{end_to_end_id = Seq});
-
-make_prepare_header(Mask, #diameter_header{hop_by_hop_id = undefined} = H) ->
- Seq = diameter_session:sequence(Mask),
- make_prepare_header(H#diameter_header{hop_by_hop_id = Seq});
-
-make_prepare_header(_, Hdr) ->
- make_prepare_header(Hdr).
-
-%% make_prepare_header/1
-
-make_prepare_header(#diameter_header{version = undefined} = Hdr) ->
- make_prepare_header(Hdr#diameter_header{version = ?DIAMETER_VERSION});
-
-make_prepare_header(#diameter_header{} = Hdr) ->
- Hdr;
-
-make_prepare_header(T) ->
+ #diameter_header{version = ?DIAMETER_VERSION,
+ end_to_end_id = Seq,
+ hop_by_hop_id = Seq};
+
+make_prepare_header(Mask, #diameter_header{version = V,
+ end_to_end_id = EI,
+ hop_by_hop_id = HI}
+ = H)
+ when EI == undefined;
+ HI == undefined ->
+ Id = diameter_session:sequence(Mask),
+ H#diameter_header{version = ?DEFAULT(V, ?DIAMETER_VERSION),
+ end_to_end_id = ?DEFAULT(EI, Id),
+ hop_by_hop_id = ?DEFAULT(HI, Id)};
+
+make_prepare_header(_, #diameter_header{version = undefined} = H) ->
+ H#diameter_header{version = ?DIAMETER_VERSION};
+
+make_prepare_header(_, #diameter_header{} = H) ->
+ H;
+
+make_prepare_header(_, T) ->
?ERROR({invalid_header, T}).
%% make_request_packet/2
@@ -1446,42 +1553,46 @@ make_retransmit_header(Hdr) ->
Hdr#diameter_header{is_retransmitted = true}.
%% fold_record/2
+%%
+%% Replace elements in the first record by those in the second that
+%% differ from undefined.
-fold_record(undefined, R) ->
- R;
-fold_record(Rec, R) ->
- diameter_lib:fold_tuple(2, Rec, R).
+fold_record(Rec0, undefined) ->
+ Rec0;
+fold_record(Rec0, Rec) ->
+ list_to_tuple(fold(tuple_to_list(Rec0), tuple_to_list(Rec))).
+
+fold([], []) ->
+ [];
+fold([H | T0], [undefined | T]) ->
+ [H | fold(T0, T)];
+fold([_ | T0], [H | T]) ->
+ [H | fold(T0, T)].
%% send_R/6
-send_R(Pkt0,
- {TPid, Caps, #diameter_app{dictionary = AppDict} = App},
- Opts,
+send_R(ReqPkt,
+ EncPkt,
+ {{TPid, _Caps} = TC, #diameter_app{dictionary = AppDict}},
+ #options{timeout = Timeout},
{Pid, Ref},
- SvcName,
- Fs) ->
- Pkt = encode(AppDict, TPid, Pkt0, Fs),
-
- #options{timeout = Timeout}
- = Opts,
-
+ Count,
+ SvcName) ->
Req = #request{ref = Ref,
caller = Pid,
handler = self(),
- transport = TPid,
- caps = Caps,
- packet = Pkt0},
+ peer = TC,
+ packet = ReqPkt},
- incr(send, Pkt, TPid, AppDict),
- {TRef, MRef} = zend_requezt(TPid, Pkt, Req, SvcName, Timeout),
+ Count andalso incr(send, EncPkt, TPid, AppDict),
+ {TRef, MRef} = zend_requezt(TPid, EncPkt, Req, SvcName, Timeout),
Pid ! Ref, %% tell caller a send has been attempted
- handle_answer(SvcName,
- App,
- recv_A(Timeout, SvcName, App, Opts, {TRef, MRef, Req})).
+ {TRef, MRef, Req}.
-%% recv_A/5
+%% recv_answer/4
-recv_A(Timeout, SvcName, App, Opts, {TRef, MRef, #request{ref = Ref} = Req}) ->
+recv_answer(SvcName, App, CallOpts, {TRef, MRef, #request{ref = Ref}
+ = Req}) ->
%% Matching on TRef below ensures we ignore messages that pertain
%% to a previous transport prior to failover. The answer message
%% includes the pid of the transport on which it was received,
@@ -1492,97 +1603,98 @@ recv_A(Timeout, SvcName, App, Opts, {TRef, MRef, #request{ref = Ref} = Req}) ->
{timeout = Reason, TRef, _} -> %% No timely reply
{error, Req, Reason};
{'DOWN', MRef, process, _, _} when false /= MRef -> %% local peer_down
- failover(SvcName, App, Req, Opts, Timeout);
+ failover(SvcName, App, Req, CallOpts);
{failover, TRef} -> %% local or remote peer_down
- failover(SvcName, App, Req, Opts, Timeout)
+ failover(SvcName, App, Req, CallOpts)
end.
-%% failover/5
+%% failover/4
-failover(SvcName, App, Req, Opts, Timeout) ->
- retransmit(pick_peer(SvcName, App, Req, Opts),
- Req,
- Opts,
- SvcName,
- Timeout).
+failover(SvcName, App, Req, CallOpts) ->
+ resend_request(pick_peer(SvcName, App, Req, CallOpts),
+ Req,
+ CallOpts,
+ SvcName).
-%% handle_answer/3
+%% handle_answer/5
-handle_answer(SvcName, App, {error, Req, Reason}) ->
- handle_error(App, Req, Reason, SvcName);
+handle_answer(SvcName, _, _, App, {error, Req, Reason}) ->
+ #request{packet = Pkt,
+ peer = {_TPid, _Caps} = TC}
+ = Req,
+ cb(App, handle_error, [Reason, msg(Pkt), SvcName, TC]);
handle_answer(SvcName,
- #diameter_app{dictionary = AppDict,
- id = Id}
+ Count,
+ SvcOpts,
+ #diameter_app{id = Id,
+ dictionary = AppDict,
+ options = [{answer_errors, AE} | _]}
= App,
{answer, Req, Dict0, Pkt}) ->
MsgDict = msg_dict(AppDict, Dict0, Pkt),
- handle_A(errors(Id, diameter_codec:decode({MsgDict, AppDict}, Pkt)),
- SvcName,
- MsgDict,
- Dict0,
- App,
- Req).
+ DecPkt = errors(Id, diameter_codec:decode({MsgDict, AppDict},
+ SvcOpts,
+ Pkt)),
+ #request{peer = {TPid, _}}
+ = Req,
-%% We don't really need to do a full decode if we're a relay and will
-%% just resend with a new hop by hop identifier, but might a proxy
-%% want to examine the answer?
-
-handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
- AppDict = App#diameter_app.dictionary,
+ answer(answer(DecPkt, TPid, MsgDict, AppDict, Dict0, Count),
+ SvcName,
+ App,
+ AE,
+ Req).
- incr(recv, Pkt, TPid, AppDict),
+%% answer/6
- try
- incr_result(recv, Pkt, TPid, {Dict, AppDict, Dict0}) %% count incoming
- of
- _ -> answer(Pkt, SvcName, App, Req)
+answer(DecPkt, TPid, MsgDict, AppDict, Dict0, Count) ->
+ Count andalso incr(recv, DecPkt, TPid, AppDict),
+ try get_result(recv, MsgDict, Dict0, DecPkt) of
+ Avp ->
+ Count andalso false /= Avp
+ andalso incr_result(recv, Avp, DecPkt, TPid, AppDict),
+ DecPkt
catch
exit: {no_result_code, _} ->
%% RFC 6733 requires one of Result-Code or
- %% Experimental-Result, but the decode will have detected
- %% a missing AVP. If both are optional in the dictionary
- %% then this isn't a decode error: just continue on.
- answer(Pkt, SvcName, App, Req);
+ %% Experimental-Result, but the decode will have
+ %% detected a missing AVP. If both are optional in
+ %% the dictionary then this isn't a decode error:
+ %% just continue on.
+ DecPkt;
exit: {invalid_error_bit, {_, _, _, Avp}} ->
#diameter_packet{errors = Es}
- = Pkt,
+ = DecPkt,
E = {5004, Avp},
- answer(Pkt#diameter_packet{errors = [E|Es]}, SvcName, App, Req)
+ DecPkt#diameter_packet{errors = [E|Es]}
end.
-%% answer/4
+%% answer/5
-answer(Pkt,
+answer(#diameter_packet{errors = Es}
+ = Pkt,
SvcName,
- #diameter_app{module = ModX,
- options = [{answer_errors, AE} | _]},
- Req) ->
- a(Pkt, SvcName, ModX, AE, Req).
-
--spec a(_, _, _) -> no_return(). %% silence dialyzer
-
-a(#diameter_packet{errors = Es}
- = Pkt,
- SvcName,
- ModX,
- AE,
- #request{transport = TPid,
- caps = Caps,
- packet = P})
- when [] == Es;
- callback == AE ->
- cb(ModX, handle_answer, [Pkt, msg(P), SvcName, {TPid, Caps}]);
-
-a(Pkt, SvcName, _, AE, _) ->
- a(Pkt#diameter_packet.header, SvcName, AE).
-
-a(Hdr, SvcName, report) ->
+ App,
+ AE,
+ #request{peer = {_TPid, _Caps} = TC,
+ packet = P})
+ when callback == AE;
+ [] == Es ->
+ cb(App, handle_answer, [Pkt, msg(P), SvcName, TC]);
+
+answer(#diameter_packet{header = H}, SvcName, _, AE, _) ->
+ handle_error(H, SvcName, AE).
+
+%% handle_error/3
+
+-spec handle_error(_, _, _) -> no_return(). %% silence dialyzer
+
+handle_error(Hdr, SvcName, report) ->
MFA = {?MODULE, handle_answer, [SvcName, Hdr]},
diameter_lib:warning_report(errors, MFA),
- a(Hdr, SvcName, discard);
+ handle_error(Hdr, SvcName, discard);
-a(Hdr, SvcName, discard) ->
+handle_error(Hdr, SvcName, discard) ->
x({answer_errors, {SvcName, Hdr}}).
%% Note that we don't check that the application id in the answer's
@@ -1593,16 +1705,38 @@ a(Hdr, SvcName, discard) ->
%% timer value is ignored. This means that an answer could be accepted
%% from a peer after timeout in the case of failover.
-%% retransmit/5
+%% resend_request/4
-retransmit({{_,_,App} = Transport, _, _}, Req, Opts, SvcName, Timeout) ->
- try retransmit(Transport, Req, SvcName, Timeout) of
- T -> recv_A(Timeout, SvcName, App, Opts, T)
- catch
- ?FAILURE(Reason) -> {error, Req, Reason}
+resend_request({{{TPid, _Caps} = TC, App}, SvcOpts},
+ Req0,
+ #options{timeout = Timeout}
+ = CallOpts,
+ SvcName) ->
+ case
+ undefined == get(TPid)
+ andalso prepare_retransmit(TC, App, Req0, SvcName)
+ of
+ [ReqPkt | Fs] ->
+ AppDict = App#diameter_app.dictionary,
+ EncPkt = encode(AppDict, TPid, SvcOpts, ReqPkt),
+ eval_packet(EncPkt, Fs),
+ Req = Req0#request{peer = TC,
+ packet = ReqPkt},
+ ?LOG(retransmission, EncPkt#diameter_packet.header),
+ incr(TPid, {msg_id(EncPkt, AppDict), send, retransmission}),
+ {TRef, MRef} = zend_requezt(TPid, EncPkt, Req, SvcName, Timeout),
+ recv_answer(SvcName, App, CallOpts, {TRef, MRef, Req});
+ false ->
+ {error, Req0, timeout};
+ {discard, Reason} ->
+ {error, Req0, Reason};
+ discard ->
+ {error, Req0, discarded};
+ {error, T} ->
+ ?ERROR({invalid_return, T, prepare_retransmit, App})
end;
-retransmit(_, Req, _, _, _) -> %% no alternate peer
+resend_request(_, Req, _, _) -> %% no alternate peer
{error, Req, failover}.
%% pick_peer/4
@@ -1612,8 +1746,8 @@ retransmit(_, Req, _, _, _) -> %% no alternate peer
pick_peer(SvcName,
App,
#request{packet = #diameter_packet{msg = Msg}},
- Opts) ->
- pick_peer(SvcName, App, Msg, Opts#options{extra = []});
+ CallOpts) ->
+ pick_peer(SvcName, App, Msg, CallOpts#options{extra = []});
pick_peer(_, _, undefined, _) ->
{error, no_connection};
@@ -1621,28 +1755,14 @@ pick_peer(_, _, undefined, _) ->
pick_peer(SvcName,
AppOrAlias,
Msg,
- #options{filter = Filter, extra = Xtra}) ->
- pick(diameter_service:pick_peer(SvcName,
- AppOrAlias,
- {fun(D) -> get_destination(D, Msg) end,
- Filter,
- Xtra})).
-
-pick(false) ->
- {error, no_connection};
-
-pick(T) ->
- T.
-
-%% handle_error/4
-
-handle_error(App,
- #request{packet = Pkt,
- transport = TPid,
- caps = Caps},
- Reason,
- SvcName) ->
- cb(App, handle_error, [Reason, msg(Pkt), SvcName, {TPid, Caps}]).
+ #options{peers = TPids, filter = Filter, extra = Xtra}) ->
+ X = {fun(D) -> get_destination(D, Msg) end, Filter, Xtra, TPids},
+ case diameter_service:pick_peer(SvcName, AppOrAlias, X) of
+ false ->
+ {error, no_connection};
+ T ->
+ T
+ end.
msg(#diameter_packet{msg = undefined, bin = Bin}) ->
Bin;
@@ -1651,27 +1771,20 @@ msg(#diameter_packet{msg = Msg}) ->
%% encode/4
-encode(Dict, TPid, Pkt, Fs) ->
- P = encode(Dict, TPid, Pkt),
- eval_packet(P, Fs),
- P.
-
-%% encode/2
-
%% Note that prepare_request can return a diameter_packet containing a
%% header or transport_data. Even allow the returned record to contain
%% an encoded binary. This isn't the usual case and doesn't properly
%% support retransmission but is useful for test.
-encode(Dict, TPid, Pkt)
+encode(Dict, TPid, Opts, Pkt)
when is_atom(Dict) ->
- encode({Dict, Dict}, TPid, Pkt);
+ encode({Dict, Dict}, TPid, Opts, Pkt);
%% A message to be encoded.
-encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) ->
+encode(DictT, TPid, Opts, #diameter_packet{bin = undefined} = Pkt) ->
{Dict, AppDict} = DictT,
try
- diameter_codec:encode(Dict, Pkt)
+ diameter_codec:encode(Dict, Opts, Pkt)
catch
exit: {diameter_codec, encode, T} = Reason ->
incr_error(send, T, TPid, AppDict),
@@ -1679,7 +1792,7 @@ encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) ->
end;
%% An encoded binary: just send.
-encode(_, _, #diameter_packet{} = Pkt) ->
+encode(_, _, _, #diameter_packet{} = Pkt) ->
Pkt.
%% zend_requezt/5
@@ -1745,81 +1858,26 @@ recv(TPid, Pid, TRef, {LocalTRef, MRef}) ->
exit({timeout, LocalTRef, TPid} = T)
end.
-%% send/2
-
-send(Pid, Pkt) ->
- Pid ! {send, Pkt}.
-
%% send/3
send(Pid, Pkt, Route) ->
Pid ! {send, Pkt, Route}.
-%% retransmit/4
+%% prepare_retransmit/4
-retransmit({TPid, Caps, App}
- = Transport,
- #request{packet = Pkt0}
- = Req,
- SvcName,
- Timeout) ->
- undefined == get(TPid) %% Don't failover to a peer we've
- orelse ?THROW(timeout), %% already sent to.
+prepare_retransmit({_TPid, _Caps} = TC, App, Req, SvcName) ->
+ Pkt = make_retransmit_packet(Req#request.packet),
- Pkt = make_retransmit_packet(Pkt0),
+ case prepare(cb(App, prepare_retransmit, [Pkt, SvcName, TC]), []) of
+ [Msg | Fs] ->
+ [make_request_packet(Msg, Pkt) | Fs];
+ No ->
+ No
+ end.
- retransmit(cb(App, prepare_retransmit, [Pkt, SvcName, {TPid, Caps}]),
- Transport,
- Req#request{packet = Pkt},
- SvcName,
- Timeout,
- []).
%% When sending a binary, it's up to prepare_retransmit to modify it
%% accordingly.
-retransmit({send, Msg},
- Transport,
- #request{packet = Pkt}
- = Req,
- SvcName,
- Timeout,
- Fs) ->
- resend_request(make_request_packet(Msg, Pkt),
- Transport,
- Req,
- SvcName,
- Timeout,
- Fs);
-
-retransmit({discard, Reason}, _, _, _, _, _) ->
- ?THROW(Reason);
-
-retransmit(discard, _, _, _, _, _) ->
- ?THROW(discarded);
-
-retransmit({eval_packet, RC, F}, Transport, Req, SvcName, Timeout, Fs) ->
- retransmit(RC, Transport, Req, SvcName, Timeout, [F|Fs]);
-
-retransmit(T, {_, _, App}, _, _, _, _) ->
- ?ERROR({invalid_return, T, prepare_retransmit, App}).
-
-resend_request(Pkt0,
- {TPid, Caps, #diameter_app{dictionary = AppDict}},
- Req0,
- SvcName,
- Tmo,
- Fs) ->
- Pkt = encode(AppDict, TPid, Pkt0, Fs),
-
- Req = Req0#request{transport = TPid,
- packet = Pkt0,
- caps = Caps},
-
- ?LOG(retransmission, Pkt#diameter_packet.header),
- incr(TPid, {msg_id(Pkt, AppDict), send, retransmission}),
- {TRef, MRef} = zend_requezt(TPid, Pkt, Req, SvcName, Tmo),
- {TRef, MRef, Req}.
-
%% peer_monitor/2
peer_monitor(TPid, TRef) ->
@@ -1837,10 +1895,8 @@ get_destination(Dict, Msg) ->
[str(get_avp_value(Dict, D, Msg)) || D <- ['Destination-Realm',
'Destination-Host']].
-%% This is not entirely correct. The avp could have an arity 1, in
-%% which case an empty list is a DiameterIdentity of length 0 rather
-%% than the list of no values we treat it as by mapping to undefined.
-%% This behaviour is documented.
+%% A DiameterIdentity has length at least one, so an empty list is not
+%% a Realm/Host.
str([]) ->
undefined;
str(T) ->
@@ -1848,16 +1904,12 @@ str(T) ->
%% get_avp/3
%%
-%% Find an AVP in a message of one of three forms:
-%%
-%% - a message record (as generated from a .dia spec) or
-%% - a list of an atom message name followed by 2-tuple, avp name/value pairs.
-%% - a list of a #diameter_header{} followed by #diameter_avp{} records,
-%%
-%% In the first two forms a dictionary module is used at encode to
-%% identify the type of the AVP and its arity in the message in
-%% question. The third form allows messages to be sent as is, without
-%% a dictionary, which is needed in the case of relay agents, for one.
+%% Find an AVP in a message in one of the decoded formats, or as a
+%% header/avps list. There are only four AVPs that are extracted here:
+%% Result-Code and Experimental-Result in order when constructing
+%% counter keys, and Destination-Host/Realm when selecting a next-hop
+%% peer. Experimental-Result is the only of type Grouped, and is given
+%% special treatment in order to return the value as a record.
%% Messages will be header/avps list as a relay and the only AVP's we
%% look for are in the common dictionary. This is required since the
@@ -1866,37 +1918,58 @@ str(T) ->
get_avp(?RELAY, Name, Msg) ->
get_avp(?BASE, Name, Msg);
-%% Message as a header/avps list.
+%% Message as header/avps list.
get_avp(Dict, Name, [#diameter_header{} | Avps]) ->
try
- {Code, _, VId} = Dict:avp_header(Name),
- find_avp(Code, VId, Avps)
- of
- A ->
- (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name}
+ {Code, _, Vid} = Dict:avp_header(Name),
+ A = find_avp(Code, Vid, Avps),
+ avp_decode(Dict, Name, ungroup(A))
catch
error: _ ->
undefined
end;
-%% Outgoing message as a name/values list.
+%% Message as name/values list ...
get_avp(_, Name, [_MsgName | Avps]) ->
- case lists:keyfind(Name, 1, Avps) of
+ case find(Name, Avps) of
{_, V} ->
- #diameter_avp{name = Name, value = V};
+ #diameter_avp{name = Name, value = value(Name, V)};
_ ->
undefined
end;
-%% Message is typically a record but not necessarily.
+%% ... or record.
get_avp(Dict, Name, Rec) ->
- try
- #diameter_avp{name = Name, value = Dict:'#get-'(Name, Rec)}
+ try Dict:'#get-'(Name, Rec) of
+ V ->
+ #diameter_avp{name = Name, value = value(Name, V)}
catch
error:_ ->
undefined
end.
+value('Experimental-Result' = N, #{'Vendor-Id' := Vid,
+ 'Experimental-Result-Code' := RC}) ->
+ {N, Vid, RC};
+value('Experimental-Result' = N, [{'Experimental-Result-Code', RC},
+ {'Vendor-Id', Vid}]) ->
+ {N, Vid, RC};
+value('Experimental-Result' = N, [{'Vendor-Id', Vid},
+ {'Experimental-Result-Code', RC}]) ->
+ {N, Vid, RC};
+value(_, V) ->
+ V.
+
+%% find/2
+
+find(Key, Map)
+ when is_map(Map) ->
+ maps:find(Key, Map);
+
+find(Key, List)
+ when is_list(List) ->
+ lists:keyfind(Key, 1, List).
+
%% get_avp_value/3
get_avp_value(Dict, Name, Msg) ->
@@ -1916,22 +1989,27 @@ ungroup(Avp) ->
%% avp_decode/3
+%% Ensure Experimental-Result is decoded as record, since this format
+%% is used for counter keys.
+avp_decode(Dict, 'Experimental-Result' = N, #diameter_avp{data = Bin}
+ = Avp)
+ when is_binary(Bin) ->
+ {V,_} = Dict:avp(decode, Bin, N, decode_opts(Dict)),
+ Avp#diameter_avp{name = N, value = V};
+
avp_decode(Dict, Name, #diameter_avp{value = undefined,
data = Bin}
- = Avp) ->
- try Dict:avp(decode, Bin, Name) of
- V ->
- Avp#diameter_avp{value = V}
- catch
- error:_ ->
- Avp
- end;
-avp_decode(_, _, #diameter_avp{} = Avp) ->
- Avp.
+ = Avp)
+ when is_binary(Bin) ->
+ V = Dict:avp(decode, Bin, Name, decode_opts(Dict)),
+ Avp#diameter_avp{name = Name, value = V};
+
+avp_decode(_, Name, #diameter_avp{} = Avp) ->
+ Avp#diameter_avp{name = Name}.
+
+%% cb/3
cb(#diameter_app{module = [_|_] = M}, F, A) ->
- eval(M, F, A);
-cb([_|_] = M, F, A) ->
eval(M, F, A).
eval([M|X], F, A) ->
@@ -1939,3 +2017,12 @@ eval([M|X], F, A) ->
choose(true, X, _) -> X;
choose(false, _, X) -> X.
+
+%% Decode options sufficient for AVP extraction.
+decode_opts(Dict) ->
+ #{decode_format => record,
+ string_decode => false,
+ strict_mbit => false,
+ failed_avp => false,
+ module => Dict,
+ app_dictionary => Dict}.
diff --git a/lib/diameter/src/base/diameter_types.erl b/lib/diameter/src/base/diameter_types.erl
index 6ecf385239..86b674dd48 100644
--- a/lib/diameter/src/base/diameter_types.erl
+++ b/lib/diameter/src/base/diameter_types.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,32 +26,16 @@
%%
%% Basic types.
--export(['OctetString'/2,
- 'Integer32'/2,
- 'Integer64'/2,
- 'Unsigned32'/2,
- 'Unsigned64'/2,
- 'Float32'/2,
- 'Float64'/2]).
-
-%% Derived types.
--export(['Address'/2,
- 'Time'/2,
- 'UTF8String'/2,
- 'DiameterIdentity'/2,
- 'DiameterURI'/2,
- 'IPFilterRule'/2,
- 'QoSFilterRule'/2]).
-
-%% Functions taking the AVP name in question as second parameter.
-export(['OctetString'/3,
'Integer32'/3,
'Integer64'/3,
'Unsigned32'/3,
'Unsigned64'/3,
'Float32'/3,
- 'Float64'/3,
- 'Address'/3,
+ 'Float64'/3]).
+
+%% Derived types.
+-export(['Address'/3,
'Time'/3,
'UTF8String'/3,
'DiameterIdentity'/3,
@@ -89,81 +73,80 @@
%% AVP Data Format is needed, a new version of this RFC must be created.
%% --------------------
-'OctetString'(decode, Bin)
+'OctetString'(decode, Bin, #{string_decode := true})
when is_binary(Bin) ->
- case diameter_codec:getopt(string_decode) of
- true ->
- binary_to_list(Bin);
- false ->
- Bin
- end;
-
-'OctetString'(decode, B) ->
+ binary_to_list(Bin);
+
+'OctetString'(decode, Bin, _)
+ when is_binary(Bin) ->
+ Bin;
+
+'OctetString'(decode, B, _) ->
?INVALID_LENGTH(B);
-'OctetString'(encode = M, zero) ->
- 'OctetString'(M, []);
+'OctetString'(encode, zero, _) ->
+ <<>>;
-'OctetString'(encode, Str) ->
+'OctetString'(encode, Str, _) ->
iolist_to_binary(Str).
%% --------------------
-'Integer32'(decode, <<X:32/signed>>) ->
+'Integer32'(decode, <<X:32/signed>>, _) ->
X;
-'Integer32'(decode, B) ->
+'Integer32'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Integer32'(encode = M, zero) ->
- 'Integer32'(M, 0);
+'Integer32'(encode, zero, _) ->
+ <<0:32/signed>>;
-'Integer32'(encode, I)
+'Integer32'(encode, I, _)
when ?SINT(32,I) ->
<<I:32/signed>>.
%% --------------------
-'Integer64'(decode, <<X:64/signed>>) ->
+'Integer64'(decode, <<X:64/signed>>, _) ->
X;
-'Integer64'(decode, B) ->
+'Integer64'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Integer64'(encode = M, zero) ->
- 'Integer64'(M, 0);
+'Integer64'(encode, zero, _) ->
+ <<0:64/signed>>;
-'Integer64'(encode, I)
+'Integer64'(encode, I, _)
when ?SINT(64,I) ->
<<I:64/signed>>.
%% --------------------
-'Unsigned32'(decode, <<X:32>>) ->
+'Unsigned32'(decode, <<X:32>>, _) ->
X;
-'Unsigned32'(decode, B) ->
+'Unsigned32'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Unsigned32'(encode = M, zero) ->
- 'Unsigned32'(M, 0);
+'Unsigned32'(encode, zero, _) ->
+ <<0:32>>;
-'Unsigned32'(encode, I)
+'Unsigned32'(encode, I, _)
when ?UINT(32,I) ->
<<I:32>>.
%% --------------------
-'Unsigned64'(decode, <<X:64>>) ->
+'Unsigned64'(decode, <<X:64>>, _) ->
X;
-'Unsigned64'(decode, B) ->
+'Unsigned64'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Unsigned64'(encode = M, zero) ->
- 'Unsigned64'(M, 0);
+'Unsigned64'(encode, zero, _) ->
+ <<0:64>>;
-'Unsigned64'(encode, I)
+'Unsigned64'(encode, I, _)
when ?UINT(64,I) ->
<<I:64>>.
@@ -184,25 +167,25 @@
%% arithmetic is performed on the decoded value. Better to be explicit
%% that precision has been lost.
-'Float32'(decode, <<S:1, 255:8, _:23>>) ->
+'Float32'(decode, <<S:1, 255:8, _:23>>, _) ->
choose(S, infinity, '-infinity');
-'Float32'(decode, <<X:32/float>>) ->
+'Float32'(decode, <<X:32/float>>, _) ->
X;
-'Float32'(decode, B) ->
+'Float32'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Float32'(encode = M, zero) ->
- 'Float32'(M, 0.0);
+'Float32'(encode, zero, _) ->
+ <<0.0:32/float>>;
-'Float32'(encode, infinity) ->
+'Float32'(encode, infinity, _) ->
<<0:1, 255:8, 0:23>>;
-'Float32'(encode, '-infinity') ->
+'Float32'(encode, '-infinity', _) ->
<<1:1, 255:8, 0:23>>;
-'Float32'(encode, X)
+'Float32'(encode, X, _)
when is_float(X) ->
<<X:32/float>>.
%% Note that this could also encode infinity/-infinity for large
@@ -222,25 +205,25 @@
%% The 64 bit format is entirely analogous to the 32 bit format.
-'Float64'(decode, <<S:1, 2047:11, _:52>>) ->
+'Float64'(decode, <<S:1, 2047:11, _:52>>, _) ->
choose(S, infinity, '-infinity');
-'Float64'(decode, <<X:64/float>>) ->
+'Float64'(decode, <<X:64/float>>, _) ->
X;
-'Float64'(decode, B) ->
+'Float64'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Float64'(encode, infinity) ->
+'Float64'(encode, infinity, _) ->
<<0:1, 2047:11, 0:52>>;
-'Float64'(encode, '-infinity') ->
+'Float64'(encode, '-infinity', _) ->
<<1:1, 2047:11, 0:52>>;
-'Float64'(encode = M, zero) ->
- 'Float64'(M, 0.0);
+'Float64'(encode, zero, _) ->
+ <<0.0:64/float>>;
-'Float64'(encode, X)
+'Float64'(encode, X, _)
when is_float(X) ->
<<X:64/float>>.
@@ -256,18 +239,18 @@
%% format.
%% --------------------
-'Address'(encode, zero) ->
+'Address'(encode, zero, _) ->
<<0:48>>;
-'Address'(decode, <<A:16, B/binary>>)
+'Address'(decode, <<A:16, B/binary>>, _)
when 1 == A, 4 == size(B);
2 == A, 16 == size(B) ->
list_to_tuple([N || <<N:A/unit:8>> <= B]);
-'Address'(decode, B) ->
+'Address'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Address'(encode, T) ->
+'Address'(encode, T, _) ->
Ns = tuple_to_list(diameter_lib:ipaddr(T)), %% length 4 or 8
A = length(Ns) div 4, %% 1 or 2
B = << <<N:A/unit:8>> || N <- Ns >>,
@@ -278,36 +261,38 @@
%% A DiameterIdentity is a FQDN as definined in RFC 1035, which is at
%% least one character.
-'DiameterIdentity'(encode = M, zero) ->
- 'OctetString'(M, [0]);
+'DiameterIdentity'(encode, zero, _) ->
+ <<0>>;
-'DiameterIdentity'(encode = M, X) ->
- <<_,_/binary>> = 'OctetString'(M, X);
+'DiameterIdentity'(encode = M, X, Opts) ->
+ <<_,_/binary>> = 'OctetString'(M, X, Opts);
-'DiameterIdentity'(decode = M, <<_,_/binary>> = X) ->
- 'OctetString'(M, X);
+'DiameterIdentity'(decode = M, <<_,_/binary>> = X, Opts) ->
+ 'OctetString'(M, X, Opts);
-'DiameterIdentity'(decode, X) ->
+'DiameterIdentity'(decode, X, _) ->
?INVALID_LENGTH(X).
%% --------------------
-'DiameterURI'(decode, Bin)
+'DiameterURI'(decode, Bin, Opts)
when is_binary(Bin) ->
- scan_uri(Bin);
+ scan_uri(Bin, Opts);
-'DiameterURI'(decode, B) ->
+'DiameterURI'(decode, B, _) ->
?INVALID_LENGTH(B);
%% The minimal DiameterURI is "aaa://x", 7 characters.
-'DiameterURI'(encode = M, zero) ->
- 'OctetString'(M, lists:duplicate(0,7));
-
-'DiameterURI'(encode, #diameter_uri{type = Type,
- fqdn = DN,
- port = PN,
- transport = T,
- protocol = P})
+'DiameterURI'(encode, zero, _) ->
+ <<0:7/unit:8>>;
+
+'DiameterURI'(encode,
+ #diameter_uri{type = Type,
+ fqdn = DN,
+ port = PN,
+ transport = T,
+ protocol = P},
+ _)
when (Type == 'aaa' orelse Type == 'aaas'),
is_integer(PN),
0 =< PN,
@@ -324,48 +309,47 @@
%% defaults, so it's best to be explicit. Interpret defaults on decode
%% since there's no choice.
-'DiameterURI'(encode, Str) ->
+'DiameterURI'(encode, Str, Opts) ->
Bin = iolist_to_binary(Str),
- #diameter_uri{} = scan_uri(Bin), %% assert
+ #diameter_uri{} = scan_uri(Bin, Opts), %% assert
Bin.
%% --------------------
%% This minimal rule is "deny in 0 from 0.0.0.0 to 0.0.0.0", 33 characters.
-'IPFilterRule'(encode = M, zero) ->
- 'OctetString'(M, lists:duplicate(0,33));
+'IPFilterRule'(encode, zero, _) ->
+ <<0:33/unit:8>>;
-'IPFilterRule'(M, X) ->
- 'OctetString'(M, X).
+'IPFilterRule'(M, X, Opts) ->
+ 'OctetString'(M, X, Opts).
%% --------------------
%% This minimal rule is the same as for an IPFilterRule.
-'QoSFilterRule'(encode = M, zero = X) ->
- 'IPFilterRule'(M, X);
+'QoSFilterRule'(encode, zero, _) ->
+ <<0:33/unit:8>>;
-'QoSFilterRule'(M, X) ->
- 'OctetString'(M, X).
+'QoSFilterRule'(M, X, Opts) ->
+ 'OctetString'(M, X, Opts).
%% --------------------
-'UTF8String'(decode, Bin)
+'UTF8String'(decode, Bin, #{string_decode := true})
when is_binary(Bin) ->
- case diameter_codec:getopt(string_decode) of
- true ->
- %% assert list return
- tl([0|_] = unicode:characters_to_list([0, Bin]));
- false ->
- <<_/binary>> = unicode:characters_to_binary(Bin)
- end;
-
-'UTF8String'(decode, B) ->
+ %% assert list return
+ tl([0|_] = unicode:characters_to_list([0, Bin]));
+
+'UTF8String'(decode, Bin, _)
+ when is_binary(Bin) ->
+ <<_/binary>> = unicode:characters_to_binary(Bin);
+
+'UTF8String'(decode, B, _) ->
?INVALID_LENGTH(B);
-'UTF8String'(encode = M, zero) ->
- 'UTF8String'(M, []);
+'UTF8String'(encode, zero, _) ->
+ <<>>;
-'UTF8String'(encode, S) ->
+'UTF8String'(encode, S, _) ->
<<_/binary>> = unicode:characters_to_binary(S). %% assert binary return
%% --------------------
@@ -414,67 +398,23 @@
-define(TIME_MIN, {{1968,1,20},{3,14,8}}). %% TIME_1900 + 1 bsl 31
-define(TIME_MAX, {{2104,2,26},{9,42,24}}). %% TIME_2036 + 1 bsl 31
-'Time'(decode, <<Time:32>>) ->
+'Time'(decode, <<Time:32>>, _) ->
Offset = msb(1 == Time bsr 31),
calendar:gregorian_seconds_to_datetime(Time + Offset);
-'Time'(decode, B) ->
+'Time'(decode, B, _) ->
?INVALID_LENGTH(B);
-'Time'(encode, {{_Y,_M,_D},{_HH,_MM,_SS}} = Datetime)
+'Time'(encode, {{_Y,_M,_D},{_HH,_MM,_SS}} = Datetime, _)
when ?TIME_MIN =< Datetime, Datetime < ?TIME_MAX ->
S = calendar:datetime_to_gregorian_seconds(Datetime),
T = S - msb(S < ?TIME_2036),
0 = T bsr 32, %% sanity check
<<T:32>>;
-'Time'(encode, zero) ->
+'Time'(encode, zero, _) ->
<<0:32>>.
-%% -------------------------------------------------------------------------
-
-'OctetString'(M, _, Data) ->
- 'OctetString'(M, Data).
-
-'Integer32'(M, _, Data) ->
- 'Integer32'(M, Data).
-
-'Integer64'(M, _, Data) ->
- 'Integer64'(M, Data).
-
-'Unsigned32'(M, _, Data) ->
- 'Unsigned32'(M, Data).
-
-'Unsigned64'(M, _, Data) ->
- 'Unsigned64'(M, Data).
-
-'Float32'(M, _, Data) ->
- 'Float32'(M, Data).
-
-'Float64'(M, _, Data) ->
- 'Float64'(M, Data).
-
-'Address'(M, _, Data) ->
- 'Address'(M, Data).
-
-'Time'(M, _, Data) ->
- 'Time'(M, Data).
-
-'UTF8String'(M, _, Data) ->
- 'UTF8String'(M, Data).
-
-'DiameterIdentity'(M, _, Data) ->
- 'DiameterIdentity'(M, Data).
-
-'DiameterURI'(M, _, Data) ->
- 'DiameterURI'(M, Data).
-
-'IPFilterRule'(M, _, Data) ->
- 'IPFilterRule'(M, Data).
-
-'QoSFilterRule'(M, _, Data) ->
- 'QoSFilterRule'(M, Data).
-
%% ===========================================================================
%% ===========================================================================
@@ -564,7 +504,7 @@ msb(false) -> ?TIME_2036.
%%
%% aaa-protocol = ( "diameter" / "radius" / "tacacs+" )
-scan_uri(Bin) ->
+scan_uri(Bin, Opts) ->
RE = "^(aaas?)://"
"([-a-zA-Z0-9.]{1,255})"
"(:0{0,5}([0-9]{1,5}))?"
@@ -583,28 +523,30 @@ scan_uri(Bin) ->
RE,
[{capture, [1,2,4,6,8], binary}]),
Type = to_atom(A),
- {PN0, T0} = defaults(diameter_codec:getopt(rfc), Type),
- PortNr = to_int(PN, PN0),
- 0 = PortNr bsr 16, %% assert
#diameter_uri{type = Type,
- fqdn = 'OctetString'(decode, DN),
- port = PortNr,
- transport = to_atom(T, T0),
+ fqdn = 'OctetString'(decode, DN, Opts),
+ port = portnr(PN, Type, Opts),
+ transport = transport(T, Opts),
protocol = to_atom(P, diameter)}.
%% Choose defaults based on the RFC, since 6733 has changed them.
-defaults(3588, _) ->
- {3868, sctp};
-defaults(6733, aaa) ->
- {3868, tcp};
-defaults(6733, aaas) ->
- {5658, tcp}.
-
-to_int(<<>>, N) ->
- N;
-to_int(B, _) ->
+
+portnr(<<>>, aaa, #{rfc := 6733}) ->
+ 3868;
+portnr(<<>>, aaas, #{rfc := 6733}) ->
+ 5868;
+portnr(<<>>, _, #{rfc := 3588}) ->
+ 3868;
+portnr(B, _, _) ->
binary_to_integer(B).
+transport(<<>>, #{rfc := 6733}) ->
+ tcp;
+transport(<<>>, #{rfc := 3588}) ->
+ sctp;
+transport(B, _) ->
+ to_atom(B).
+
to_atom(<<>>, A) ->
A;
to_atom(B, _) ->
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index f28b8f2910..43623334a9 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -50,10 +50,6 @@
-define(IS_NATURAL(N), (is_integer(N) andalso 0 =< N)).
--record(config,
- {suspect = 1 :: non_neg_integer(), %% OKAY -> SUSPECT
- okay = 3 :: non_neg_integer()}). %% REOPEN -> OKAY
-
-record(watchdog,
{%% PCB - Peer Control Block; see RFC 3539, Appendix A
status = initial :: initial | okay | suspect | down | reopen,
@@ -70,12 +66,18 @@
| integer() %% monotonic time
| undefined,
dictionary :: module(), %% common dictionary
- receive_data :: term(),
- %% term passed into diameter_service with incoming message
- sequence :: diameter:sequence(), %% mask
- restrict :: {diameter:restriction(), boolean()},
- shutdown = false :: boolean(),
- config :: #config{}}).
+ receive_data :: term(), %% term passed with incoming message
+ config :: #{sequence := diameter:sequence(), %% mask
+ restrict_connections := diameter:restriction(),
+ restrict := boolean(),
+ suspect := non_neg_integer(), %% OKAY -> SUSPECT
+ okay := non_neg_integer()}, %% REOPEN -> OKAY
+ codec :: #{decode_format := none,
+ string_decode := false,
+ strict_mbit := boolean(),
+ rfc := 3588 | 6733,
+ ordered_encode := false},
+ shutdown = false :: boolean()}).
%% ---------------------------------------------------------------------------
%% start/2
@@ -85,12 +87,12 @@
%% reason.
%% ---------------------------------------------------------------------------
--spec start(Type, {RecvData, [Opt], SvcOpts, #diameter_service{}})
+-spec start(Type, {[Opt], SvcOpts, RecvData, #diameter_service{}})
-> {reference(), pid()}
when Type :: {connect|accept, diameter:transport_ref()},
- RecvData :: term(),
Opt :: diameter:transport_opt(),
- SvcOpts :: [diameter:service_opt()].
+ SvcOpts :: map(),
+ RecvData :: term().
start({_,_} = Type, T) ->
Ack = make_ref(),
@@ -117,22 +119,21 @@ init(T) ->
proc_lib:init_ack({ok, self()}),
gen_server:enter_loop(?MODULE, [], i(T)).
-i({Ack, T, Pid, {RecvData,
- Opts,
- SvcOpts,
+i({Ack, T, Pid, {Opts,
+ #{restrict_connections := Restrict}
+ = SvcOpts0,
+ RecvData,
#diameter_service{applications = Apps,
capabilities = Caps}
= Svc}}) ->
monitor(process, Pid),
wait(Ack, Pid),
+
+ Dict0 = common_dictionary(Apps),
+ SvcOpts = SvcOpts0#{rfc => rfc(Dict0)},
putr(restart, {T, Opts, Svc, SvcOpts}), %% save seeing it in trace
putr(dwr, dwr(Caps)), %%
- {_,_} = Mask = proplists:get_value(sequence, SvcOpts),
- Restrict = proplists:get_value(restrict_connections, SvcOpts),
Nodes = restrict_nodes(Restrict),
- Dict0 = common_dictionary(Apps),
- diameter_codec:setopts([{common_dictionary, Dict0},
- {string_decode, false}]),
#watchdog{parent = Pid,
transport = start(T, Opts, SvcOpts, Nodes, Dict0, Svc),
tw = proplists:get_value(watchdog_timer,
@@ -140,9 +141,23 @@ i({Ack, T, Pid, {RecvData,
?DEFAULT_TW_INIT),
receive_data = RecvData,
dictionary = Dict0,
- sequence = Mask,
- restrict = {Restrict, lists:member(node(), Nodes)},
- config = config(Opts)}.
+ config = maps:with([sequence,
+ restrict_connections,
+ restrict,
+ suspect,
+ okay],
+ config(SvcOpts#{restrict => restrict(Nodes),
+ suspect => 1,
+ okay => 3},
+ Opts)),
+ codec = maps:with([decode_format,
+ strict_mbit,
+ string_decode,
+ rfc,
+ ordered_encode],
+ SvcOpts#{decode_format := none,
+ string_decode := false,
+ ordered_encode => false})}.
wait(Ref, Pid) ->
receive
@@ -152,22 +167,31 @@ wait(Ref, Pid) ->
exit({shutdown, D})
end.
-%% config/1
+%% Regard anything but the generated RFC 3588 dictionary as modern.
+%% This affects the interpretation of defaults during the decode
+%% of values of type DiameterURI, this having changed from RFC 3588.
+%% (So much for backwards compatibility.)
+rfc(?BASE) ->
+ 3588;
+rfc(_) ->
+ 6733.
+
+%% config/2
%%
%% Could also configure counts for SUSPECT to DOWN and REOPEN to DOWN,
%% but don't.
-config(Opts) ->
+config(Map, Opts) ->
Config = proplists:get_value(watchdog_config, Opts, []),
- lists:foldl(fun config/2, #config{}, Config).
+ lists:foldl(fun cfg/2, Map, Config).
-config({suspect, N}, Rec)
+cfg({suspect, N}, Map)
when ?IS_NATURAL(N) ->
- Rec#config{suspect = N};
+ Map#{suspect := N};
-config({okay, N}, Rec)
+cfg({okay, N}, Map)
when ?IS_NATURAL(N) ->
- Rec#config{okay = N}.
+ Map#{okay := N}.
%% start/6
@@ -283,7 +307,7 @@ event(Msg,
?LOG(transition, {From, To}).
data(Msg, TPid, reopen, okay) ->
- {recv, TPid, false, 'DWA', _Pkt, _NPid} = Msg, %% assert
+ {recv, TPid, _, 'DWA', _Pkt} = Msg, %% assert
{TPid, T} = eraser(open),
[T];
@@ -302,6 +326,8 @@ tpid(_, Pid)
tpid(Pid, _) ->
Pid.
+%% send/2
+
send(Pid, T) ->
Pid ! T.
@@ -375,8 +401,8 @@ transition({accepted = T, TPid}, #watchdog{transport = TPid,
transition({open, TPid, Hosts, _} = Open,
#watchdog{transport = TPid,
status = initial,
- restrict = {_,R},
- config = #config{suspect = OS}}
+ config = #{restrict := R,
+ suspect := OS}}
= S) ->
case okay(role(), Hosts, R) of
okay ->
@@ -394,8 +420,8 @@ transition({open, TPid, Hosts, _} = Open,
transition({open = Key, TPid, _Hosts, T},
#watchdog{transport = TPid,
status = down,
- config = #config{suspect = OS,
- okay = RO}}
+ config = #{suspect := OS,
+ okay := RO}}
= S) ->
case RO of
0 -> %% non-standard: skip REOPEN
@@ -428,7 +454,7 @@ transition({'DOWN', _, process, TPid, _Reason},
transition({'DOWN', _, process, TPid, _Reason} = D,
#watchdog{transport = TPid,
status = T,
- restrict = {_,R}}
+ config = #{restrict := R}}
= S0) ->
S = S0#watchdog{pending = false,
transport = undefined},
@@ -447,14 +473,15 @@ transition({'DOWN', _, process, TPid, _Reason} = D,
end;
%% Incoming message.
-transition({recv, TPid, Route, Name, Pkt, NPid},
+transition({recv, TPid, Route, Name, Pkt},
#watchdog{transport = TPid}
= S) ->
- try
- incoming(Name, Pkt, NPid, S)
- catch
+ try incoming(Route, Name, Pkt, S) of
#watchdog{dictionary = Dict0, receive_data = T} = NS ->
- diameter_traffic:receive_message(TPid, Route, Pkt, NPid, Dict0, T),
+ diameter_traffic:receive_message(TPid, Route, Pkt, Dict0, T),
+ NS
+ catch
+ #watchdog{} = NS ->
NS
end;
@@ -483,9 +510,9 @@ getr(Key) ->
eraser(Key) ->
erase({?MODULE, Key}).
-%% encode/3
+%% encode/4
-encode(dwr = M, Dict0, Mask) ->
+encode(dwr = M, Dict0, Opts, Mask) ->
Msg = getr(M),
Seq = diameter_session:sequence(Mask),
Hdr = #diameter_header{version = ?DIAMETER_VERSION,
@@ -493,10 +520,10 @@ encode(dwr = M, Dict0, Mask) ->
hop_by_hop_id = Seq},
Pkt = #diameter_packet{header = Hdr,
msg = Msg},
- diameter_codec:encode(Dict0, Pkt);
+ diameter_codec:encode(Dict0, Opts, Pkt);
-encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD}
- = ReqPkt) ->
+encode(dwa, Dict0, Opts, #diameter_packet{header = H, transport_data = TD}
+ = ReqPkt) ->
AnsPkt = #diameter_packet{header
= H#diameter_header{is_request = false,
is_error = undefined,
@@ -504,7 +531,7 @@ encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD}
msg = dwa(ReqPkt),
transport_data = TD},
- diameter_codec:encode(Dict0, AnsPkt).
+ diameter_codec:encode(Dict0, Opts, AnsPkt).
%% okay/3
@@ -574,9 +601,10 @@ tw({M,F,A}) ->
send_watchdog(#watchdog{pending = false,
transport = TPid,
dictionary = Dict0,
- sequence = Mask}
+ config = #{sequence := Mask},
+ codec = Opts}
= S) ->
- #diameter_packet{bin = Bin} = EPkt = encode(dwr, Dict0, Mask),
+ #diameter_packet{bin = Bin} = EPkt = encode(dwr, Dict0, Opts, Mask),
diameter_traffic:incr(send, EPkt, TPid, Dict0),
send(TPid, {send, Bin}),
?LOG(send, 'DWR'),
@@ -586,41 +614,30 @@ send_watchdog(#watchdog{pending = false,
%% incoming/4
-incoming(Name, Pkt, false, S) ->
- recv(Name, Pkt, S);
-
-incoming(Name, Pkt, NPid, S) ->
- try
- recv(Name, Pkt, S)
- after
- NPid ! {diameter, discard}
- end.
-
-%% recv/3
-
-recv(Name, Pkt, S) ->
- try rcv(Name, Pkt, rcv(Name, S)) of
- #watchdog{} = NS ->
- throw(NS)
+incoming(Route, Name, Pkt, S) ->
+ try rcv(Name, S) of
+ NS -> rcv(Name, Pkt, NS)
catch
- #watchdog{} = NS -> %% throwaway
- NS
+ #watchdog{transport = TPid} = NS when Route -> %% incoming request
+ send(TPid, {send, false}), %% requiring ack
+ throw(NS)
end.
%% rcv/3
rcv('DWR', Pkt, #watchdog{transport = TPid,
- dictionary = Dict0}
+ dictionary = Dict0,
+ codec = Opts}
= S) ->
?LOG(recv, 'DWR'),
- DPkt = diameter_codec:decode(Dict0, Pkt),
+ DPkt = diameter_codec:decode(Dict0, Opts, Pkt),
diameter_traffic:incr(recv, DPkt, TPid, Dict0),
diameter_traffic:incr_error(recv, DPkt, TPid, Dict0),
#diameter_packet{header = H,
transport_data = T,
bin = Bin}
= EPkt
- = encode(dwa, Dict0, Pkt),
+ = encode(dwa, Dict0, Opts, Pkt),
diameter_traffic:incr(send, EPkt, TPid, Dict0),
diameter_traffic:incr_rc(send, EPkt, TPid, Dict0),
@@ -632,12 +649,13 @@ rcv('DWR', Pkt, #watchdog{transport = TPid,
throw(S);
rcv('DWA', Pkt, #watchdog{transport = TPid,
- dictionary = Dict0}
+ dictionary = Dict0,
+ codec = Opts}
= S) ->
?LOG(recv, 'DWA'),
diameter_traffic:incr(recv, Pkt, TPid, Dict0),
diameter_traffic:incr_rc(recv,
- diameter_codec:decode(Dict0, Pkt),
+ diameter_codec:decode(Dict0, Opts, Pkt),
TPid,
Dict0),
throw(S);
@@ -699,12 +717,12 @@ rcv(_, #watchdog{status = okay} = S) ->
%% SUSPECT Receive non-DWA Failback()
%% SetWatchdog() OKAY
-rcv('DWA', #watchdog{status = suspect, config = #config{suspect = OS}} = S) ->
+rcv('DWA', #watchdog{status = suspect, config = #{suspect := OS}} = S) ->
set_watchdog(S#watchdog{status = okay,
num_dwa = OS,
pending = false});
-rcv(_, #watchdog{status = suspect, config = #config{suspect = OS}} = S) ->
+rcv(_, #watchdog{status = suspect, config = #{suspect := OS}} = S) ->
set_watchdog(S#watchdog{status = okay,
num_dwa = OS});
@@ -714,8 +732,8 @@ rcv(_, #watchdog{status = suspect, config = #config{suspect = OS}} = S) ->
rcv('DWA', #watchdog{status = reopen,
num_dwa = N,
- config = #config{suspect = OS,
- okay = RO}}
+ config = #{suspect := OS,
+ okay := RO}}
= S)
when N+1 == RO ->
S#watchdog{status = okay,
@@ -846,18 +864,19 @@ restart(S) -> %% reconnect has won race with timeout
restart({{connect, _} = T, Opts, Svc, SvcOpts},
#watchdog{parent = Pid,
- restrict = {R,_},
+ config = #{restrict_connections := R}
+ = M,
dictionary = Dict0}
= S) ->
send(Pid, {reconnect, self()}),
Nodes = restrict_nodes(R),
S#watchdog{transport = start(T, Opts, SvcOpts, Nodes, Dict0, Svc),
- restrict = {R, lists:member(node(), Nodes)}};
+ config = M#{restrict => restrict(Nodes)}};
%% No restriction on the number of connections to the same peer: just
%% die. Note that a state machine never enters state REOPEN in this
%% case.
-restart({{accept, _}, _, _, _}, #watchdog{restrict = {_, false}}) ->
+restart({{accept, _}, _, _, _}, #watchdog{config = #{restrict := false}}) ->
stop;
%% Otherwise hang around until told to die, either by the service or
@@ -901,3 +920,8 @@ restrict_nodes(Nodes)
restrict_nodes(F) ->
diameter_lib:eval(F).
+
+%% restrict/1
+
+restrict(Nodes) ->
+ lists:member(node(), Nodes).
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index 864d5f0691..4e6fe32d69 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -21,15 +21,14 @@
-module(diameter_codegen).
%%
-%% This module generates erl/hrl files for encode/decode modules
-%% from the orddict parsed from a dictionary file (.dia) by
-%% diameter_dict_util. The generated code is simple (one-liners),
-%% the generated functions being called by code included iin the
-%% generated modules from diameter_gen.hrl. The orddict itself is
-%% returned by dict/0 in the generated module and diameter_dict_util
-%% calls this function when importing dictionaries as a consequence
-%% of @inherits sections. That is, @inherits introduces a dependency
-%% on the beam file of another dictionary.
+%% This module generates erl/hrl files for encode/decode modules from
+%% the orddict parsed from a dictionary file by diameter_dict_util.
+%% The generated code is simple (one-liners), and is called from
+%% diameter_gen. The orddict itself is returned by dict/0 in the
+%% generated module and diameter_dict_util calls this function when
+%% importing dictionaries as a consequence of @inherits sections. That
+%% is, @inherits introduces a dependency on the beam file of another
+%% dictionary.
%%
-export([from_dict/4,
@@ -150,20 +149,21 @@ erl_forms(Mod, ParseD) ->
{id, 0},
{vendor_id, 0},
{vendor_name, 0},
- {decode_avps, 2}, %% in diameter_gen.hrl
- {encode_avps, 2}, %%
+ {decode_avps, 3}, %% in diameter_gen.hrl
+ {encode_avps, 3}, %%
+ {grouped_avp, 4}, %%
{msg_name, 2},
{msg_header, 1},
{rec2msg, 1},
{msg2rec, 1},
{name2rec, 1},
{avp_name, 2},
+ {avp_arity, 1},
{avp_arity, 2},
{avp_header, 1},
- {avp, 3},
- {grouped_avp, 3},
+ {avp, 4},
{enumerated_avp, 3},
- {empty_value, 1},
+ {empty_value, 2},
{dict, 0}]},
%% diameter.hrl is included for #diameter_avp
{?attribute, include_lib, "diameter/include/diameter.hrl"},
@@ -178,13 +178,14 @@ erl_forms(Mod, ParseD) ->
f_msg2rec(ParseD),
f_name2rec(ParseD),
f_avp_name(ParseD),
- f_avp_arity(ParseD),
+ f_avp_arity_1(ParseD),
+ f_avp_arity_2(ParseD),
f_avp_header(ParseD),
f_avp(ParseD),
f_enumerated_avp(ParseD),
f_empty_value(ParseD),
f_dict(ParseD),
- {eof, erl_anno:new(?LINE)}]],
+ {eof, ?LINE}]],
lists:append(Forms).
@@ -418,10 +419,32 @@ vendor_id_map(ParseD) ->
get_value(grouped, ParseD)).
%%% ------------------------------------------------------------------------
+%%% # avp_arity/1
+%%% ------------------------------------------------------------------------
+
+f_avp_arity_1(ParseD) ->
+ {?function, avp_arity, 1, avp_arities(ParseD) ++ [?BADARG(1)]}.
+
+avp_arities(ParseD) ->
+ Msgs = get_value(messages, ParseD),
+ Groups = get_value(grouped, ParseD)
+ ++ lists:flatmap(fun avps/1, get_value(import_groups, ParseD)),
+ lists:map(fun c_avp_arities/1, Msgs ++ Groups).
+
+c_avp_arities({N,_,_,_,As}) ->
+ c_avp_arities(N,As);
+c_avp_arities({N,_,_,As}) ->
+ c_avp_arities(N,As).
+
+c_avp_arities(Name, Avps) ->
+ Arities = [{?A(N), A} || T <- Avps, {N,A} <- [avp_info(T)]],
+ {?clause, [?Atom(Name)], [], [?TERM(Arities)]}.
+
+%%% ------------------------------------------------------------------------
%%% # avp_arity/2
%%% ------------------------------------------------------------------------
-f_avp_arity(ParseD) ->
+f_avp_arity_2(ParseD) ->
{?function, avp_arity, 2, avp_arity(ParseD)}.
avp_arity(ParseD) ->
@@ -452,7 +475,7 @@ c_arity(Name, Avp) ->
%%% ------------------------------------------------------------------------
f_avp(ParseD) ->
- {?function, avp, 3, avp(ParseD) ++ [?BADARG(3)]}.
+ {?function, avp, 4, avp(ParseD) ++ [?BADARG(4)]}.
avp(ParseD) ->
Native = get_value(avp_types, ParseD),
@@ -491,19 +514,25 @@ avp(Native, Imported, Custom, Enums) ->
not_in(List, X) ->
not lists:member(X, List).
-c_base_avp({AvpName, T}) ->
- {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName)],
+c_base_avp({AvpName, "Enumerated"}) ->
+ {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName), ?VAR('_')],
[],
- [b_base_avp(AvpName, T)]}.
+ [?CALL(enumerated_avp, [?VAR('T'), ?Atom(AvpName), ?VAR('Data')])]};
-b_base_avp(AvpName, "Enumerated") ->
- ?CALL(enumerated_avp, [?VAR('T'), ?Atom(AvpName), ?VAR('Data')]);
-
-b_base_avp(AvpName, "Grouped") ->
- ?CALL(grouped_avp, [?VAR('T'), ?Atom(AvpName), ?VAR('Data')]);
+c_base_avp({AvpName, "Grouped"}) ->
+ {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName), ?VAR('Opts')],
+ [],
+ [?CALL(grouped_avp, [?VAR('T'),
+ ?Atom(AvpName),
+ ?VAR('Data'),
+ ?VAR('Opts')])]};
-b_base_avp(_, Type) ->
- ?APPLY(diameter_types, ?A(Type), [?VAR('T'), ?VAR('Data')]).
+c_base_avp({AvpName, Type}) ->
+ {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName), ?VAR('Opts')],
+ [],
+ [?APPLY(diameter_types, ?A(Type), [?VAR('T'),
+ ?VAR('Data'),
+ ?VAR('Opts')])]}.
cs_imported_avp({Mod, Avps}, Enums, CustomNames) ->
lists:map(fun(A) -> imported_avp(Mod, A, Enums) end,
@@ -525,11 +554,13 @@ imported_avp(Mod, {AvpName, _, _, _}, _) ->
c_imported_avp(Mod, AvpName).
c_imported_avp(Mod, AvpName) ->
- {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName)],
+ {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName), ?VAR('Opts')],
[],
- [?APPLY(Mod, avp, [?VAR('T'),
- ?VAR('Data'),
- ?Atom(AvpName)])]}.
+ [?CALL(avp, [?VAR('T'),
+ ?VAR('Data'),
+ ?Atom(AvpName),
+ ?VAR('Opts'),
+ ?ATOM(Mod)])]}.
cs_custom_avp({Mod, Key, Avps}, Dict) ->
lists:map(fun(N) -> c_custom_avp(Mod, Key, N, orddict:fetch(N, Dict)) end,
@@ -537,9 +568,12 @@ cs_custom_avp({Mod, Key, Avps}, Dict) ->
c_custom_avp(Mod, Key, AvpName, Type) ->
{F,A} = custom(Key, AvpName, Type),
- {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName)],
+ {?clause, [?VAR('T'), ?VAR('Data'), ?Atom(AvpName), ?VAR('Opts')],
[],
- [?APPLY(?A(Mod), ?A(F), [?VAR('T'), ?Atom(A), ?VAR('Data')])]}.
+ [?APPLY(?A(Mod), ?A(F), [?VAR('T'),
+ ?Atom(A),
+ ?VAR('Data'),
+ ?VAR('Opts')])]}.
custom(custom_types, AvpName, Type) ->
{AvpName, Type};
@@ -568,7 +602,11 @@ enumerated_avp(Mod, Es, Enums) ->
Es).
cs_enumerated_avp(true, Mod, Name) ->
- [c_imported_avp(Mod, Name)];
+ [{?clause, [?VAR('T'), ?Atom(Name), ?VAR('Data')],
+ [],
+ [?APPLY(Mod, enumerated_avp, [?VAR('T'),
+ ?Atom(Name),
+ ?VAR('Data')])]}];
cs_enumerated_avp(false, _, _) ->
[].
@@ -682,7 +720,7 @@ v(false, _, _, _) ->
%%% ------------------------------------------------------------------------
f_empty_value(ParseD) ->
- {?function, empty_value, 1, empty_value(ParseD)}.
+ {?function, empty_value, 2, empty_value(ParseD)}.
empty_value(ParseD) ->
Imported = lists:flatmap(fun avps/1, get_value(import_enums, ParseD)),
@@ -692,15 +730,17 @@ empty_value(ParseD) ->
not lists:keymember(N, 1, Imported)]
++ Imported,
lists:map(fun c_empty_value/1, Groups ++ Enums)
- ++ [{?clause, [?VAR('Name')], [], [?CALL(empty, [?VAR('Name')])]}].
+ ++ [{?clause, [?VAR('Name'), ?VAR('Opts')],
+ [],
+ [?CALL(empty, [?VAR('Name'), ?VAR('Opts')])]}].
c_empty_value({Name, _, _, _}) ->
- {?clause, [?Atom(Name)],
+ {?clause, [?Atom(Name), ?VAR('Opts')],
[],
- [?CALL(empty_group, [?Atom(Name)])]};
+ [?CALL(empty_group, [?Atom(Name), ?VAR('Opts')])]};
c_empty_value({Name, _}) ->
- {?clause, [?Atom(Name)],
+ {?clause, [?Atom(Name), ?VAR('_')],
[],
[?TERM(<<0:32>>)]}.
diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl
index f9f2b02e94..7b53e51cb6 100644
--- a/lib/diameter/src/compiler/diameter_dict_util.erl
+++ b/lib/diameter/src/compiler/diameter_dict_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -923,7 +923,7 @@ xa([D|_] = Ds, [[Qual, D, {_, Line, AvpName}] | Avps], Dict, Key, Name) ->
store_new({Key, {Name, AvpName}},
[Line, Qual, D],
Dict,
- [Name, Line],
+ [AvpName, Line],
avp_already_referenced),
Key,
Name);
diff --git a/lib/diameter/src/compiler/diameter_exprecs.erl b/lib/diameter/src/compiler/diameter_exprecs.erl
index 9a0cb6baf2..ded07f2353 100644
--- a/lib/diameter/src/compiler/diameter_exprecs.erl
+++ b/lib/diameter/src/compiler/diameter_exprecs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -110,9 +110,9 @@
%% parse_transform/2
parse_transform(Forms, _Options) ->
- Rs = [R || {attribute, _, record, R} <- Forms],
- Es = lists:append([E || {attribute, _, export_records, E} <- Forms]),
{H,T} = lists:splitwith(fun is_head/1, Forms),
+ Rs = [R || {attribute, _, record, R} <- H],
+ Es = lists:append([E || {attribute, _, export_records, E} <- H]),
H ++ [a_export(Es) | f_accessors(Es, Rs)] ++ T.
is_head(T) ->
diff --git a/lib/diameter/src/compiler/diameter_make.erl b/lib/diameter/src/compiler/diameter_make.erl
index eae40dbafd..03cfc03edc 100644
--- a/lib/diameter/src/compiler/diameter_make.erl
+++ b/lib/diameter/src/compiler/diameter_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -271,6 +271,6 @@ make(File, Opts, Dict, Mode) ->
try
diameter_codegen:from_dict(File, Dict, Opts, Mode)
catch
- error: Reason ->
- erlang:error({Reason, Mode, erlang:get_stacktrace()})
+ error: Reason: Stack ->
+ erlang:error({Reason, Mode, Stack})
end.
diff --git a/lib/diameter/src/diameter.app.src b/lib/diameter/src/diameter.app.src
index d380ebbd92..18202f033e 100644
--- a/lib/diameter/src/diameter.app.src
+++ b/lib/diameter/src/diameter.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,14 +28,21 @@
]},
{registered, [%REGISTERED%]},
{applications, [
- {stdlib, "2.0"}, {kernel, "3.0"}%, {erts, "6.0"}
- %% {syntax-tools, "1.6.14"}
- %% {runtime-tools, "1.8.14"}
- %, {ssl, "5.3.4"}
+ stdlib,
+ kernel
+ %, ssl
+ %, syntax-tools
+ %, runtime-tools
]},
{env, []},
{mod, {diameter_app, []}},
{runtime_dependencies, [
+ "erts-10.0",
+ "stdlib-2.4",
+ "kernel-3.2",
+ "ssl-9.0"
+ %, "syntax-tools-1.6.18"
+ %, "runtime-tools-1.8.16"
]}
%%
%% Note that ssl is only required if configured on TCP transports,
diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src
index eb5a5a44f3..51830f5276 100644
--- a/lib/diameter/src/diameter.appup.src
+++ b/lib/diameter/src/diameter.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -51,7 +51,15 @@
{"1.11.1", [{restart_application, diameter}]}, %% 18.2
{"1.11.2", [{restart_application, diameter}]}, %% 18.3
{"1.12", [{restart_application, diameter}]}, %% 19.0
- {"1.12.1", [{restart_application, diameter}]} %% 19.1
+ {"1.12.1", [{restart_application, diameter}]}, %% 19.1
+ {"1.12.2", [{restart_application, diameter}]}, %% 19.3
+ {"2.0", [{restart_application, diameter}]}, %% 20.0
+ {"2.1", [{restart_application, diameter}]}, %% 20.1
+ {"2.1.1", [{restart_application, diameter}]}, %% 20.1.2
+ {"2.1.2", [{restart_application, diameter}]}, %% 20.1.3
+ {"2.1.3", [{restart_application, diameter}]}, %% 20.2
+ {"2.1.4", [{restart_application, diameter}]}, %% 20.3
+ {"2.1.5", [{update, diameter_peer_fsm}]} %% 21.0
],
[
{"0.9", [{restart_application, diameter}]},
@@ -84,6 +92,14 @@
{"1.11.1", [{restart_application, diameter}]},
{"1.11.2", [{restart_application, diameter}]},
{"1.12", [{restart_application, diameter}]},
- {"1.12.1", [{restart_application, diameter}]}
+ {"1.12.1", [{restart_application, diameter}]},
+ {"1.12.2", [{restart_application, diameter}]},
+ {"2.0", [{restart_application, diameter}]},
+ {"2.1", [{restart_application, diameter}]},
+ {"2.1.1", [{restart_application, diameter}]},
+ {"2.1.2", [{restart_application, diameter}]},
+ {"2.1.3", [{restart_application, diameter}]},
+ {"2.1.4", [{restart_application, diameter}]},
+ {"2.1.5", [{update, diameter_peer_fsm}]}
]
}.
diff --git a/lib/diameter/src/dict/doic_rfc7683.dia b/lib/diameter/src/dict/doic_rfc7683.dia
new file mode 100644
index 0000000000..2b7804115e
--- /dev/null
+++ b/lib/diameter/src/dict/doic_rfc7683.dia
@@ -0,0 +1,50 @@
+;;
+;; %CopyrightBegin%
+;;
+;; Copyright Ericsson AB 2017. All Rights Reserved.
+;;
+;; Licensed under the Apache License, Version 2.0 (the "License");
+;; you may not use this file except in compliance with the License.
+;; You may obtain a copy of the License at
+;;
+;; http://www.apache.org/licenses/LICENSE-2.0
+;;
+;; Unless required by applicable law or agreed to in writing, software
+;; distributed under the License is distributed on an "AS IS" BASIS,
+;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;; See the License for the specific language governing permissions and
+;; limitations under the License.
+;;
+;; %CopyrightEnd%
+;;
+
+@name diameter_gen_doic_rfc7683
+@prefix diameter_doic
+
+@avp_types
+
+ OC-Supported-Features 621 Grouped -
+ OC-Feature-Vector 622 Unsigned64 -
+ OC-OLR 623 Grouped -
+ OC-Sequence-Number 624 Unsigned64 -
+ OC-Validity-Duration 625 Unsigned32 -
+ OC-Report-Type 626 Enumerated -
+ OC-Reduction-Percentage 627 Unsigned32 -
+
+@enum OC-Report-Type
+
+ HOST_REPORT 0
+ REALM_REPORT 1
+
+@grouped
+
+ OC-Supported-Features ::= < AVP Header: 621 >
+ [ OC-Feature-Vector ]
+ * [ AVP ]
+
+ OC-OLR ::= < AVP Header: 623 >
+ < OC-Sequence-Number >
+ < OC-Report-Type >
+ [ OC-Reduction-Percentage ]
+ [ OC-Validity-Duration ]
+ * [ AVP ]
diff --git a/lib/diameter/src/info/diameter_info.erl b/lib/diameter/src/info/diameter_info.erl
index 59a3b94ee4..23a42e48fd 100644
--- a/lib/diameter/src/info/diameter_info.erl
+++ b/lib/diameter/src/info/diameter_info.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -195,7 +195,7 @@ format(Tables, SFun, CFun)
%%%
%%% Description: Pretty-print records in a named tables as collected
%%% from local and remote nodes. Each table listing is
-%%% preceeded by a banner.
+%%% preceded by a banner.
%%% ----------------------------------------------------------
format(Local, Remote, SFun) ->
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index 4e4ce60ddf..bb86de016a 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -1,7 +1,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2016. All Rights Reserved.
+# Copyright Ericsson AB 2010-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ DICTS = \
base_rfc6733 \
base_accounting \
acct_rfc6733 \
+ doic_rfc7683 \
relay
# The yecc grammar for the dictionary parser.
@@ -39,7 +40,7 @@ RT_MODULES = \
base/diameter_config \
base/diameter_config_sup \
base/diameter_codec \
- base/diameter_dict \
+ base/diameter_gen \
base/diameter_lib \
base/diameter_misc_sup \
base/diameter_peer \
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index f48e4347ee..64b34da690 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -52,21 +52,20 @@
%% Keys into process dictionary.
-define(INFO_KEY, info).
-define(REF_KEY, ref).
+-define(TRANSPORT_KEY, transport).
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
%% The default port for a listener.
-define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1
-%% Remote addresses to accept connections from.
--define(DEFAULT_ACCEPT, []). %% any
-
%% How long to wait for a transport process to attach after
%% association establishment.
-define(ACCEPT_TIMEOUT, 5000).
-type connect_option() :: {raddr, inet:ip_address()}
| {rport, inet:port_number()}
+ | option()
| term(). %% gen_sctp:open_option().
-type match() :: inet:ip_address()
@@ -74,8 +73,14 @@
| [match()].
-type listen_option() :: {accept, match()}
+ | option()
| term(). %% gen_sctp:open_option().
+-type option() :: {sender, boolean()}
+ | sender
+ | {packet, boolean() | raw}
+ | {message_cb, false | diameter:eval()}.
+
-type uint() :: non_neg_integer().
%% Accepting/connecting transport process state.
@@ -87,20 +92,38 @@
%% {RAs, RP, Errors}
| connect,
socket :: gen_sctp:sctp_socket() | undefined,
- assoc_id :: gen_sctp:assoc_id(), %% association identifier
+ active = false :: boolean(), %% is socket active?
+ recv = true :: boolean(), %% should it be active?
+ assoc_id :: gen_sctp:assoc_id() %% association identifier
+ | undefined
+ | true,
peer :: {[inet:ip_address()], uint()} %% {RAs, RP}
| undefined,
streams :: {uint(), uint()} %% {InStream, OutStream} counts
| undefined,
- os = 0 :: uint()}). %% next output stream
+ os = 0 :: uint(), %% next output stream
+ rotate = 1 :: boolean() | 0 | 1, %% rotate os?
+ unordered = false :: boolean() %% always send unordered?
+ | pos_integer(),% or if =< N outbound streams?
+ packet = true :: boolean() %% legacy transport_data?
+ | raw,
+ message_cb = false :: false | diameter:eval(),
+ send = false :: pid() | boolean()}). %% sending process
+
+%% Monitor process state.
+-record(monitor,
+ {transport :: pid(),
+ ack = false :: boolean(),
+ socket :: gen_sctp:sctp_socket(),
+ assoc_id :: gen_sctp:assoc_id()}).
%% Listener process state.
-record(listener,
{ref :: reference(),
socket :: gen_sctp:sctp_socket(),
- service = false :: false | pid(), %% service process
+ service :: pid(), %% service process
pending = {0, queue:new()},
- accept :: [match()]}).
+ opts :: [[match()] | boolean() | diameter:eval()]}).
%% Field pending implements two queues: the first of transport-to-be
%% processes to which an association has been assigned but for which
%% diameter hasn't yet spawned a transport process, a short-lived
@@ -132,24 +155,19 @@
start(T, Svc, Opts)
when is_list(Opts) ->
#diameter_service{capabilities = Caps,
- pid = SPid}
+ pid = Pid}
= Svc,
diameter_sctp_sup:start(), %% start supervisors on demand
Addrs = Caps#diameter_caps.host_ip_address,
- s(T, Addrs, SPid, lists:map(fun ip/1, Opts)).
-
-ip({ifaddr, A}) ->
- {ip, A};
-ip(T) ->
- T.
+ s(T, Addrs, Pid, Opts).
%% A listener spawns transports either as a consequence of this call
%% when there is not yet an association to assign it, or at comm_up on
%% a new association in which case the call retrieves a transport from
%% the pending queue.
-s({accept, Ref} = A, Addrs, SPid, Opts) ->
- {ok, LPid, LAs} = listener(Ref, {Opts, Addrs}),
- try gen_server:call(LPid, {A, self(), SPid}, infinity) of
+s({accept, Ref} = A, Addrs, SvcPid, Opts) ->
+ {ok, LPid, LAs} = listener(Ref, {Opts, SvcPid, Addrs}),
+ try gen_server:call(LPid, {A, self()}, infinity) of
{ok, TPid} ->
{ok, TPid, LAs};
No ->
@@ -162,7 +180,7 @@ s({accept, Ref} = A, Addrs, SPid, Opts) ->
%% gen_sctp in order to be able to accept a new association only
%% *after* an accepting transport has been spawned.
-s({connect = C, Ref}, Addrs, _SPid, Opts) ->
+s({connect = C, Ref}, Addrs, _SvcPid, Opts) ->
diameter_sctp_sup:start_child({C, self(), Opts, Addrs, Ref}).
%% start_link/1
@@ -216,22 +234,46 @@ init(T) ->
%% i/1
+i(#monitor{transport = TPid} = S) ->
+ monitor(process, TPid),
+ putr(?TRANSPORT_KEY, TPid),
+ proc_lib:init_ack({ok, self()}),
+ S;
+
%% A process owning a listening socket.
-i({listen, Ref, {Opts, Addrs}}) ->
+i({listen, Ref, {Opts, SvcPid, Addrs}}) ->
+ monitor(process, SvcPid),
[_] = diameter_config:subscribe(Ref, transport), %% assert existence
- {[Matches], Rest} = proplists:split(Opts, [accept]),
+ {Split, Rest} = proplists:split(Opts, [accept,
+ packet,
+ sender,
+ message_cb,
+ unordered]),
+ OwnOpts = lists:append(Split),
{LAs, Sock} = AS = open(Addrs, Rest, ?DEFAULT_PORT),
ok = gen_sctp:listen(Sock, true),
true = diameter_reg:add_new({?MODULE, listener, {Ref, AS}}),
proc_lib:init_ack({ok, self(), LAs}),
#listener{ref = Ref,
+ service = SvcPid,
socket = Sock,
- accept = [[M] || {accept, M} <- Matches]};
+ opts = [[[M] || {accept, M} <- OwnOpts],
+ proplists:get_value(packet, OwnOpts, true)
+ | [proplists:get_value(K, OwnOpts, false)
+ || K <- [sender, message_cb, unordered]]]};
%% A connecting transport.
i({connect, Pid, Opts, Addrs, Ref}) ->
- {[As, Ps], Rest} = proplists:split(Opts, [raddr, rport]),
- RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- As],
+ {[Ps | Split], Rest} = proplists:split(Opts, [rport,
+ raddr,
+ packet,
+ sender,
+ message_cb,
+ unordered]),
+ OwnOpts = lists:append(Split),
+ CB = proplists:get_value(message_cb, OwnOpts, false),
+ false == CB orelse (Pid ! {diameter, ack}),
+ RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- OwnOpts],
[RP] = [P || {rport, P} <- Ps] ++ [P || P <- [?DEFAULT_PORT], [] == Ps],
{LAs, Sock} = open(Addrs, Rest, 0),
putr(?REF_KEY, Ref),
@@ -239,7 +281,11 @@ i({connect, Pid, Opts, Addrs, Ref}) ->
monitor(process, Pid),
#transport{parent = Pid,
mode = {connect, connect(Sock, RAs, RP, [])},
- socket = Sock};
+ socket = Sock,
+ message_cb = CB,
+ unordered = proplists:get_value(ordered, OwnOpts, false),
+ packet = proplists:get_value(packet, OwnOpts, true),
+ send = proplists:get_value(sender, OwnOpts, false)};
%% An accepting transport spawned by diameter, not yet owning an
%% association.
@@ -273,11 +319,17 @@ i({K, Ref}, #transport{mode = {accept, _}} = S) ->
receive
{Ref, Pid} when K == parent -> %% transport process started
S#transport{parent = Pid};
- {K, T, Matches} when K == peeloff -> %% association
+ {K, T, Opts} when K == peeloff -> %% association
{sctp, Sock, _RA, _RP, _Data} = T,
+ [Matches, Packet, Sender, CB, Unordered] = Opts,
ok = accept_peer(Sock, Matches),
demonitor(Ref, [flush]),
- t(T, S#transport{socket = Sock});
+ false == CB orelse (S#transport.parent ! {diameter, ack}),
+ t(T, S#transport{socket = Sock,
+ message_cb = CB,
+ unordered = Unordered,
+ packet = Packet,
+ send = Sender});
accept_timeout = T ->
x(T);
{'DOWN', _, process, _, _} = T ->
@@ -309,23 +361,35 @@ l([], Ref, T) ->
%% open/3
open(Addrs, Opts, PortNr) ->
- {LAs, Os} = addrs(Addrs, Opts),
- {LAs, case gen_sctp:open(gen_opts(portnr(Os, PortNr))) of
- {ok, Sock} ->
- Sock;
- {error, Reason} ->
- x({open, Reason})
- end}.
+ case gen_sctp:open(gen_opts(portnr(addrs(Addrs, Opts), PortNr))) of
+ {ok, Sock} ->
+ {addrs(Sock), Sock};
+ {error, Reason} ->
+ x({open, Reason})
+ end.
addrs(Addrs, Opts) ->
- case proplists:split(Opts, [ip]) of
- {[[]], _} ->
- {Addrs, Opts ++ [{ip, A} || A <- Addrs]};
- {[As], Os} ->
- LAs = [diameter_lib:ipaddr(A) || {ip, A} <- As],
- {LAs, Os ++ [{ip, A} || A <- LAs]}
+ case lists:mapfoldl(fun ipaddr/2, false, Opts) of
+ {Os, true} ->
+ Os;
+ {_, false} ->
+ Opts ++ [{ip, A} || A <- Addrs]
end.
+ipaddr({K,A}, _)
+ when K == ifaddr;
+ K == ip ->
+ {{ip, ipaddr(A)}, true};
+ipaddr(T, B) ->
+ {T, B}.
+
+ipaddr(A)
+ when A == loopback;
+ A == any ->
+ A;
+ipaddr(A) ->
+ diameter_lib:ipaddr(A).
+
portnr(Opts, PortNr) ->
case proplists:get_value(port, Opts) of
undefined ->
@@ -334,6 +398,14 @@ portnr(Opts, PortNr) ->
Opts
end.
+addrs(Sock) ->
+ case inet:socknames(Sock) of
+ {ok, As} ->
+ [A || {A,_} <- As];
+ {error, Reason} ->
+ x({socknames, Reason})
+ end.
+
%% x/1
x(Reason) ->
@@ -374,13 +446,9 @@ handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref} = S) ->
{TPid, NewS} = accept(Ref, Pid, S),
{reply, {ok, TPid}, NewS};
-handle_call({{accept, _} = T, Pid, SPid}, From, #listener{service = P} = S) ->
- handle_call({T, Pid}, From, if not is_pid(P), is_pid(SPid) ->
- monitor(process, SPid),
- S#listener{service = SPid};
- true ->
- S
- end);
+%% Transport is telling us of parent death.
+handle_call({stop, _Pid} = Reason, _From, #monitor{} = S) ->
+ {stop, {shutdown, Reason}, ok, S};
handle_call(_, _, State) ->
{reply, nok, State}.
@@ -400,9 +468,13 @@ handle_info(T, #transport{} = S) ->
{noreply, #transport{} = t(T,S)};
handle_info(T, #listener{} = S) ->
- {noreply, #listener{} = l(T,S)}.
+ {noreply, #listener{} = l(T,S)};
+
+handle_info(T, #monitor{} = S) ->
+ m(T,S),
+ {noreply, S}.
-%% Prior to the possiblity of setting pool_size on in transport
+%% Prior to the possibility of setting pool_size on in transport
%% configuration, a new accepting transport was only started following
%% the death of a predecessor, so that there was only at most one
%% previously started transport process waiting for an association.
@@ -422,6 +494,9 @@ code_change(_, State, _) ->
%% # terminate/2
%% ---------------------------------------------------------------------------
+terminate(_, #monitor{}) ->
+ ok;
+
terminate(_, #transport{assoc_id = undefined}) ->
ok;
@@ -445,11 +520,11 @@ getr(Key) ->
%% Incoming message from SCTP.
l({sctp, Sock, _RA, _RP, Data} = T, #listener{socket = Sock,
- accept = Matches}
+ opts = Opts}
= S) ->
Id = assoc_id(Data),
{TPid, NewS} = accept(S),
- TPid ! {peeloff, setelement(2, T, peeloff(Sock, Id, TPid)), Matches},
+ TPid ! {peeloff, setelement(2, T, peeloff(Sock, Id, TPid)), Opts},
setopts(Sock),
NewS;
@@ -503,12 +578,21 @@ t(T,S) ->
%% Incoming message.
transition({sctp, Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) ->
- setopts(Sock),
- recv(Data, S);
+ setopts(S, recv(Data, S#transport{active = false}));
%% Outgoing message.
transition({diameter, {send, Msg}}, S) ->
- send(Msg, S);
+ message(send, Msg, S);
+
+%% Monitor has sent an outgoing message.
+transition(Msg, S)
+ when is_record(Msg, diameter_packet);
+ is_binary(Msg) ->
+ message(ack, Msg, S);
+
+%% Deferred actions from a message_cb.
+transition({actions, Dir, Acts}, S) ->
+ setopts(ok, actions(Acts, Dir, S));
%% Request to close the transport connection.
transition({diameter, {close, Pid}}, #transport{parent = Pid}) ->
@@ -522,8 +606,18 @@ transition({diameter, {close, Pid}}, #transport{parent = Pid}) ->
transition({diameter, {tls, _Ref, _Type, _Bool}}, _) ->
stop;
-%% Parent process has died.
-transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
+%% Parent process has died: call the monitor to not close the socket
+%% during an ongoing send, but don't let it take forever.
+transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid,
+ send = MPid}) ->
+ is_boolean(MPid)
+ orelse ok == (catch gen_server:call(MPid, {stop, Pid}))
+ orelse exit(MPid, kill),
+ stop;
+
+%% Monitor process has died.
+transition({'DOWN', _, process, MPid, _}, #transport{send = MPid})
+ when is_pid(MPid) ->
stop;
%% Timeout after transport process has been started.
@@ -536,6 +630,18 @@ transition({resolve_port, Pid}, #transport{socket = Sock})
Pid ! inet:port(Sock),
ok.
+%% m/2
+
+m({Msg, StreamId}, #monitor{socket = Sock,
+ transport = TPid,
+ assoc_id = AId,
+ ack = B}) ->
+ send(Sock, AId, StreamId, Msg),
+ B andalso (TPid ! Msg);
+
+m({'DOWN', _, process, TPid, _} = T, #monitor{transport = TPid}) ->
+ x(T).
+
%% Crash on anything unexpected.
ok({ok, T}) ->
@@ -578,33 +684,57 @@ q(Ref, Pid, #listener{pending = {_,Q}}) ->
%% send/2
+%% Start monitor process on first send.
+send(Msg, #transport{send = true,
+ socket = Sock,
+ assoc_id = AId,
+ message_cb = CB}
+ = S) ->
+ {ok, MPid} = diameter_sctp_sup:start_child(#monitor{transport = self(),
+ socket = Sock,
+ assoc_id = AId,
+ ack = false /= CB}),
+ monitor(process, MPid),
+ send(Msg, S#transport{send = MPid});
+
%% Outbound Diameter message on a specified stream ...
-send(#diameter_packet{bin = Bin, transport_data = {outstream, SId}},
+send(#diameter_packet{transport_data = {outstream, SId}}
+ = Msg,
#transport{streams = {_, OS}}
= S) ->
- send(SId rem OS, Bin, S),
- S;
+ send(SId rem OS, Msg, S);
-%% ... or not: rotate through all streams.
-send(#diameter_packet{bin = Bin}, S) ->
- send(Bin, S);
-send(Bin, #transport{streams = {_, OS},
+%% ... or not: rotate when sending on multiple streams ...
+send(Msg, #transport{rotate = true,
+ streams = {_, OS},
os = N}
- = S)
- when is_binary(Bin) ->
- send(N, Bin, S),
- S#transport{os = (N + 1) rem OS}.
+ = S) ->
+ send(N, Msg, S#transport{os = (N + 1) rem OS});
+
+%% ... or send on the only stream available.
+send(Msg, S) ->
+ send(0, Msg, S).
%% send/3
-send(StreamId, Bin, #transport{socket = Sock,
- assoc_id = AId}) ->
- send(Sock, AId, StreamId, Bin).
+send(StreamId, Msg, #transport{send = false,
+ socket = Sock,
+ assoc_id = AId}
+ = S) ->
+ send(Sock, AId, StreamId, Msg),
+ message(ack, Msg, S);
+
+send(StreamId, Msg, #transport{send = MPid} = S) ->
+ MPid ! {Msg, StreamId},
+ S.
%% send/4
-send(Sock, AssocId, Stream, Bin) ->
- case gen_sctp:send(Sock, AssocId, Stream, Bin) of
+send(Sock, AssocId, StreamId, #diameter_packet{bin = Bin}) ->
+ send(Sock, AssocId, StreamId, Bin);
+
+send(Sock, AssocId, StreamId, Bin) ->
+ case gen_sctp:send(Sock, AssocId, StreamId, Bin) of
ok ->
ok;
{error, Reason} ->
@@ -624,7 +754,9 @@ recv({_, #sctp_assoc_change{state = comm_up,
= S) ->
Ref = getr(?REF_KEY),
publish(T, Ref, Id, Sock),
- up(S#transport{assoc_id = Id,
+ %% Deal with different association id after peeloff on Solaris by
+ %% taking the id from the first reception.
+ up(S#transport{assoc_id = T == accept orelse Id,
streams = {IS, OS}});
%% ... or not: try the next address.
@@ -639,17 +771,19 @@ recv({_, #sctp_assoc_change{} = E},
recv({_, #sctp_assoc_change{}}, _) ->
stop;
+%% First inbound on an accepting transport.
+recv({[#sctp_sndrcvinfo{assoc_id = Id}], _Bin}
+ = T,
+ #transport{assoc_id = true}
+ = S) ->
+ recv(T, S#transport{assoc_id = Id});
+
%% Inbound Diameter message.
-recv({[#sctp_sndrcvinfo{stream = Id}], Bin}, #transport{parent = Pid})
+recv({[#sctp_sndrcvinfo{}], Bin} = Msg, S)
when is_binary(Bin) ->
- diameter_peer:recv(Pid, #diameter_packet{transport_data = {stream, Id},
- bin = Bin}),
- ok;
+ message(recv, Msg, recv(S));
-recv({_, #sctp_shutdown_event{assoc_id = A}},
- #transport{assoc_id = Id})
- when A == Id;
- A == 0 ->
+recv({_, #sctp_shutdown_event{}}, _) ->
stop;
%% Note that diameter_sctp(3) documents that sctp_events cannot be
@@ -667,6 +801,41 @@ recv({_, #sctp_paddr_change{}}, _) ->
recv({_, #sctp_pdapi_event{}}, _) ->
ok.
+%% recv/1
+%%
+%% Start sending unordered after the second reception, so that an
+%% outgoing CER/CEA will arrive at the peer before another request.
+
+recv(#transport{rotate = B} = S)
+ when is_boolean(B) ->
+ S;
+
+recv(#transport{rotate = 0,
+ streams = {_,OS},
+ socket = Sock,
+ unordered = B}
+ = S) ->
+ ok = unordered(Sock, OS, B),
+ S#transport{rotate = 1 < OS};
+
+recv(#transport{rotate = N} = S) ->
+ S#transport{rotate = N-1}.
+
+%% unordered/3
+
+unordered(Sock, OS, B)
+ when B;
+ is_integer(B), OS =< B ->
+ inet:setopts(Sock, [{sctp_default_send_param,
+ #sctp_sndrcvinfo{flags = [unordered]}}]);
+
+unordered(_, OS, B)
+ when not B;
+ is_integer(B), B < OS ->
+ ok.
+
+%% publish/4
+
publish(T, Ref, Id, Sock) ->
true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}),
putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1
@@ -765,6 +934,23 @@ connect(Sock, [Addr | AT] = As, Port, Reasons) ->
connect(Sock, AT, Port, [{Addr, E} | Reasons])
end.
+%% setopts/2
+
+setopts(_, #transport{socket = Sock,
+ active = A,
+ recv = B}
+ = S)
+ when B, not A ->
+ setopts(Sock),
+ S#transport{active = true};
+
+setopts(_, #transport{} = S) ->
+ S;
+
+setopts(#transport{socket = Sock}, T) ->
+ setopts(Sock),
+ T.
+
%% setopts/1
setopts(Sock) ->
@@ -772,3 +958,83 @@ setopts(Sock) ->
ok -> ok;
X -> x({setopts, Sock, X}) %% possibly on peer disconnect
end.
+
+%% A message_cb is invoked whenever a message is sent or received, or
+%% to provide acknowledgement of a completed send or discarded
+%% request. See diameter_tcp for semantics, the only difference being
+%% that a recv callback can get a diameter_packet record as Msg
+%% depending on how/if option packet has been specified.
+
+%% message/3
+
+message(send, false = M, S) ->
+ message(ack, M, S);
+
+message(ack, _, #transport{message_cb = false} = S) ->
+ S;
+
+message(Dir, Msg, S) ->
+ setopts(S, actions(cb(S, Dir, Msg), Dir, S)).
+
+%% actions/3
+
+actions([], _, S) ->
+ S;
+
+actions([B | As], Dir, S)
+ when is_boolean(B) ->
+ actions(As, Dir, S#transport{recv = B});
+
+actions([Dir | As], _, S)
+ when Dir == send;
+ Dir == recv ->
+ actions(As, Dir, S);
+
+actions([Msg | As], send = Dir, S)
+ when is_record(Msg, diameter_packet);
+ is_binary(Msg) ->
+ actions(As, Dir, send(Msg, S));
+
+actions([Msg | As], recv = Dir, #transport{parent = Pid} = S)
+ when is_record(Msg, diameter_packet);
+ is_binary(Msg) ->
+ diameter_peer:recv(Pid, Msg),
+ actions(As, Dir, S);
+
+actions([{defer, Tmo, Acts} | As], Dir, S) ->
+ erlang:send_after(Tmo, self(), {actions, Dir, Acts}),
+ actions(As, Dir, S);
+
+actions(CB, _, S) ->
+ S#transport{message_cb = CB}.
+
+%% cb/3
+
+cb(#transport{message_cb = false, packet = P}, recv, Msg) ->
+ [pkt(P, true, Msg)];
+
+cb(#transport{message_cb = CB, packet = P}, recv = D, Msg) ->
+ cb(CB, D, pkt(P, false, Msg));
+
+cb(#transport{message_cb = CB}, Dir, Msg) ->
+ cb(CB, Dir, Msg);
+
+cb(false, send, Msg) ->
+ [Msg];
+
+cb(CB, Dir, Msg) ->
+ diameter_lib:eval([CB, Dir, Msg]).
+
+%% pkt/3
+
+pkt(false, _, {_Info, Bin}) ->
+ Bin;
+
+pkt(true, _, {[#sctp_sndrcvinfo{stream = Id}], Bin}) ->
+ #diameter_packet{bin = Bin, transport_data = {stream, Id}};
+
+pkt(raw, true, {[Info], Bin}) ->
+ #diameter_packet{bin = Bin, transport_data = Info};
+
+pkt(raw, false, {[_], _} = Msg) ->
+ Msg.
diff --git a/lib/diameter/src/transport/diameter_sctp_sup.erl b/lib/diameter/src/transport/diameter_sctp_sup.erl
index 36050aaf28..e8e26ec7c5 100644
--- a/lib/diameter/src/transport/diameter_sctp_sup.erl
+++ b/lib/diameter/src/transport/diameter_sctp_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@ start() ->
start_child(T) ->
SupRef = case element(1,T) of
+ monitor -> ?TRANSPORT_SUP;
connect -> ?TRANSPORT_SUP;
accept -> ?TRANSPORT_SUP;
listen -> ?LISTENER_SUP
diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl
index 44abc5c3b4..da059fa7d6 100644
--- a/lib/diameter/src/transport/diameter_tcp.erl
+++ b/lib/diameter/src/transport/diameter_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@
%%
-module(diameter_tcp).
--dialyzer({no_fail_call, throttle/2}).
-behaviour(gen_server).
@@ -53,6 +52,7 @@
%% Keys into process dictionary.
-define(INFO_KEY, info).
-define(REF_KEY, ref).
+-define(TRANSPORT_KEY, transport).
-define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})).
@@ -68,20 +68,26 @@
%% The same gen_server implementation supports three different kinds
%% of processes: an actual transport process, one that will club it to
%% death should the parent die before a connection is established, and
-%% a process owning the listening port.
+%% a process owning the listening port. The monitor process
+%% historically died after connection establishment, but can now live
+%% on as the sender of outgoing messages, so that a blocking send
+%% doesn't prevent messages from being received.
%% Listener process state.
-record(listener, {socket :: inet:socket(),
+ module :: module(),
service = false :: false | pid()}). %% service process
%% Monitor process state.
-record(monitor,
- {parent :: pid(),
- transport = self() :: pid()}).
+ {parent :: reference() | false | pid(),
+ transport = self() :: pid(),
+ ack = false :: boolean(),
+ socket :: inet:socket() | ssl:sslsocket() | undefined,
+ module :: module() | undefined}).
-type length() :: 0..16#FFFFFF. %% message length from Diameter header
--type size() :: non_neg_integer(). %% accumulated binary size
--type frag() :: {length(), size(), binary(), list(binary())}
+-type frag() :: maybe_improper_list(length(), binary())
| binary().
-type connect_option() :: {raddr, inet:ip_address()}
@@ -97,25 +103,30 @@
-type listen_option() :: {accept, match()}
| {ssl_options, true | [ssl:listen_option()]}
+ | option()
| ssl:listen_option()
| gen_tcp:listen_option().
-type option() :: {port, non_neg_integer()}
- | {fragment_timer, 0..16#FFFFFFFF}
- | {throttle_cb, diameter:evaluable()}.
+ | {sender, boolean()}
+ | sender
+ | {message_cb, false | diameter:eval()}
+ | {fragment_timer, 0..16#FFFFFFFF}.
%% Accepting/connecting transport process state.
-record(transport,
{socket :: inet:socket() | ssl:sslsocket(), %% accept/connect socket
+ active = false :: boolean(), %% is socket active?
+ recv = true :: boolean(), %% should it be active?
parent :: pid(), %% of process that started us
module :: module(), %% gen_tcp-like module
- frag = <<>> :: frag(), %% message fragment
ssl :: [term()] | boolean(), %% ssl options, ssl or not
+ frag = <<>> :: frag(), %% message fragment
timeout :: infinity | 0..16#FFFFFFFF, %% fragment timeout
tref = false :: false | reference(), %% fragment timer reference
flush = false :: boolean(), %% flush fragment at timeout?
- throttle_cb :: false | diameter:evaluable(), %% ask to receive
- throttled :: boolean() | binary()}). %% stopped receiving?
+ message_cb :: false | diameter:eval(),
+ send :: pid() | false}). %% sending process
%% The usual transport using gen_tcp can be replaced by anything
%% sufficiently gen_tcp-like by passing a 'module' option as the first
@@ -131,19 +142,18 @@
-> {ok, pid(), [inet:ip_address()]}
when Ref :: diameter:transport_ref();
({connect, Ref}, #diameter_service{}, [connect_option()])
- -> {ok, pid(), [inet:ip_address()]}
- | {ok, pid()}
+ -> {ok, pid()}
when Ref :: diameter:transport_ref().
start({T, Ref}, Svc, Opts) ->
#diameter_service{capabilities = Caps,
- pid = SPid}
+ pid = SvcPid}
= Svc,
diameter_tcp_sup:start(), %% start tcp supervisors on demand
{Mod, Rest} = split(Opts),
Addrs = Caps#diameter_caps.host_ip_address,
- Arg = {T, Ref, Mod, self(), Rest, Addrs, SPid},
+ Arg = {T, Ref, Mod, self(), Rest, Addrs, SvcPid},
diameter_tcp_sup:start_child(Arg).
split([{module, M} | Opts]) ->
@@ -197,74 +207,63 @@ init(T) ->
%% i/1
%% A transport process.
-i({T, Ref, Mod, Pid, Opts, Addrs, SPid})
+i({T, Ref, Mod, Pid, Opts, Addrs, SvcPid})
when T == accept;
T == connect ->
monitor(process, Pid),
%% Since accept/connect might block indefinitely, spawn a process
- %% that does nothing but kill us with the parent until call
- %% returns.
- {ok, MPid} = diameter_tcp_sup:start_child(#monitor{parent = Pid}),
+ %% that kills us with the parent until call returns, and then
+ %% sends outgoing messages.
{[SO|TO], Rest} = proplists:split(Opts, [ssl_options,
- fragment_timer,
- throttle_cb]),
+ sender,
+ message_cb,
+ fragment_timer]),
SslOpts = ssl_opts(SO),
OwnOpts = lists:append(TO),
Tmo = proplists:get_value(fragment_timer,
OwnOpts,
?DEFAULT_FRAGMENT_TIMEOUT),
+ [CB, Sender] = [proplists:get_value(K, OwnOpts, false)
+ || K <- [message_cb, sender]],
?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}),
- Throttle = proplists:get_value(throttle_cb, OwnOpts, false),
- Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs, SPid),
- MPid ! {stop, self()}, %% tell the monitor to die
+ {ok, MPid} = diameter_tcp_sup:start_child(#monitor{parent = Pid}),
+ Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs, SvcPid),
M = if SslOpts -> ssl; true -> Mod end,
+ Sender andalso monitor(process, MPid),
+ false == CB orelse (Pid ! {diameter, ack}),
+ MPid ! {start, self(), Sender andalso {Sock, M}, false /= CB},
putr(?REF_KEY, Ref),
- throttle(#transport{parent = Pid,
- module = M,
- socket = Sock,
- ssl = SslOpts,
- timeout = Tmo,
- throttle_cb = Throttle,
- throttled = false /= Throttle});
+ setopts(#transport{parent = Pid,
+ module = M,
+ socket = Sock,
+ ssl = SslOpts,
+ message_cb = CB,
+ timeout = Tmo,
+ send = Sender andalso MPid});
%% Put the reference in the process dictionary since we now use it
%% advertise the ssl socket after TLS upgrade.
-i({T, _Ref, _Mod, _Pid, _Opts, _Addrs} = Arg) %% from old code
- when T == accept;
- T == connect ->
- i(erlang:append_element(Arg, _SPid = false));
-
%% A monitor process to kill the transport if the parent dies.
i(#monitor{parent = Pid, transport = TPid} = S) ->
+ putr(?TRANSPORT_KEY, TPid),
proc_lib:init_ack({ok, self()}),
- monitor(process, Pid),
monitor(process, TPid),
- S;
+ S#monitor{parent = monitor(process, Pid)};
%% In principle a link between the transport and killer processes
%% could do the same thing: have the accepting/connecting process be
%% killed when the killer process dies as a consequence of parent
%% death. However, a link can be unlinked and this is exactly what
-%% gen_tcp seems to so. Links should be left to supervisors.
-
-i({listen = L, Ref, _APid, T}) -> %% from old code
- i({L, Ref, T});
+%% gen_tcp seems to do. Links should be left to supervisors.
i({listen, Ref, {Mod, Opts, Addrs}}) ->
[_] = diameter_config:subscribe(Ref, transport), %% assert existence
- {[LA, LP], Rest} = proplists:split(Opts, [ip, port]),
- LAddrOpt = get_addr(LA, Addrs),
- LPort = get_port(LP),
- {ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)),
- LAddr = laddr(LAddrOpt, Mod, LSock),
+ {[LP], Rest} = proplists:split(Opts, [port]),
+ {ok, LSock} = Mod:listen(get_port(LP), gen_opts(Addrs, Rest)),
+ {ok, {LAddr, _}} = sockname(Mod, LSock),
true = diameter_reg:add_new({?MODULE, listener, {Ref, {LAddr, LSock}}}),
proc_lib:init_ack({ok, self(), {LAddr, LSock}}),
- #listener{socket = LSock}.
-
-laddr([], Mod, Sock) ->
- {ok, {Addr, _Port}} = sockname(Mod, Sock),
- Addr;
-laddr([{ip, Addr}], _, _) ->
- Addr.
+ #listener{socket = LSock,
+ module = Mod}.
ssl_opts([]) ->
false;
@@ -279,19 +278,19 @@ ssl_opts(T) ->
%% init/8
%% Establish a TLS connection before capabilities exchange ...
-init(Type, Ref, Mod, Pid, true, Opts, Addrs, SPid) ->
- init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs, SPid);
+init(Type, Ref, Mod, Pid, true, Opts, Addrs, SvcPid) ->
+ init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs, SvcPid);
%% ... or not.
-init(Type, Ref, Mod, Pid, _, Opts, Addrs, SPid) ->
- init(Type, Ref, Mod, Pid, Opts, Addrs, SPid).
+init(Type, Ref, Mod, Pid, _, Opts, Addrs, SvcPid) ->
+ init(Type, Ref, Mod, Pid, Opts, Addrs, SvcPid).
%% init/7
-init(accept = T, Ref, Mod, Pid, Opts, Addrs, SPid) ->
+init(accept = T, Ref, Mod, Pid, Opts, Addrs, SvcPid) ->
{[Matches], Rest} = proplists:split(Opts, [accept]),
{ok, LPid, {LAddr, LSock}} = listener(Ref, {Mod, Rest, Addrs}),
- ok = gen_server:call(LPid, {accept, SPid}, infinity),
+ ok = gen_server:call(LPid, {accept, SvcPid}, infinity),
proc_lib:init_ack({ok, self(), [LAddr]}),
Sock = ok(accept(Mod, LSock)),
ok = accept_peer(Mod, Sock, accept(Matches)),
@@ -299,25 +298,17 @@ init(accept = T, Ref, Mod, Pid, Opts, Addrs, SPid) ->
diameter_peer:up(Pid),
Sock;
-init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SPid) ->
- {[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]),
- LAddrOpt = get_addr(LA, Addrs),
+init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SvcPid) ->
+ {[RA, RP], Rest} = proplists:split(Opts, [raddr, rport]),
RAddr = get_addr(RA),
RPort = get_port(RP),
- proc_lib:init_ack(init_rc(LAddrOpt)),
- Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddrOpt, Rest))),
+ proc_lib:init_ack({ok, self()}),
+ Sock = ok(connect(Mod, RAddr, RPort, gen_opts(Addrs, Rest))),
publish(Mod, T, Ref, Sock),
- up(Pid, {RAddr, RPort}, LAddrOpt, Mod, Sock),
+ up(Pid, {RAddr, RPort}, 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) ->
+up(Pid, Remote, Mod, Sock) ->
{Addr, _Port} = ok(sockname(Mod, Sock)),
diameter_peer:up(Pid, Remote, [Addr]).
@@ -374,25 +365,41 @@ l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) ->
l([], Ref, T) ->
diameter_tcp_sup:start_child({listen, Ref, T}).
-%% get_addr/1
+%% addrs/2
+%%
+%% Take the first address from the service if several are specified
+%% and not address is configured.
+
+addrs(Addrs, Opts) ->
+ case lists:mapfoldr(fun ipaddr/2, [], Opts) of
+ {Os, [_]} ->
+ Os;
+ {_, []} ->
+ Opts ++ [{ip, A} || [A|_] <- [Addrs]];
+ {_, As} ->
+ ?ERROR({invalid_addrs, As, Addrs})
+ end.
-get_addr(As) ->
- diameter_lib:ipaddr(addr(As, [])).
+ipaddr({K,A}, As)
+ when K == ifaddr;
+ K == ip ->
+ {{ip, ipaddr(A)}, [A | As]};
+ipaddr(T, B) ->
+ {T, B}.
-%% get_addr/2
+ipaddr(A)
+ when A == loopback;
+ A == any ->
+ A;
+ipaddr(A) ->
+ diameter_lib:ipaddr(A).
-get_addr([], []) ->
- [];
-get_addr(As, Def) ->
- [{ip, diameter_lib:ipaddr(addr(As, Def))}].
+%% get_addr/1
-%% Take the first address from the service if several are unspecified.
-addr([], [Addr | _]) ->
- Addr;
-addr([{_, Addr}], _) ->
- Addr;
-addr(As, Addrs) ->
- ?ERROR({invalid_addrs, As, Addrs}).
+get_addr([{_, Addr}]) ->
+ diameter_lib:ipaddr(Addr);
+get_addr(Addrs) ->
+ ?ERROR({invalid_addrs, Addrs}).
%% get_port/1
@@ -405,10 +412,15 @@ get_port(Ps) ->
%% gen_opts/2
-gen_opts(LAddrOpt, Opts) ->
+gen_opts(Addrs, Opts) ->
+ gen_opts(addrs(Addrs, Opts)).
+
+%% gen_opts/1
+
+gen_opts(Opts) ->
{L,_} = proplists:split(Opts, [binary, packet, active]),
[[],[],[]] == L orelse ?ERROR({reserved_options, Opts}),
- [binary, {packet, 0}, {active, false}] ++ LAddrOpt ++ Opts.
+ [binary, {packet, 0}, {active, false} | Opts].
%% ---------------------------------------------------------------------------
%% # ports/1
@@ -451,14 +463,18 @@ portnr(Sock) ->
%% # handle_call/3
%% ---------------------------------------------------------------------------
-handle_call({accept, SPid}, _From, #listener{service = P} = S) ->
- {reply, ok, if not is_pid(P), is_pid(SPid) ->
- monitor(process, SPid),
- S#listener{service = SPid};
+handle_call({accept, SvcPid}, _From, #listener{service = P} = S) ->
+ {reply, ok, if not is_pid(P), is_pid(SvcPid) ->
+ monitor(process, SvcPid),
+ S#listener{service = SvcPid};
true ->
S
end};
-
+
+%% Transport is telling us of parent death.
+handle_call({stop, _Pid} = Reason, _From, #monitor{} = S) ->
+ {stop, {shutdown, Reason}, ok, S};
+
handle_call(_, _, State) ->
{reply, nok, State}.
@@ -480,8 +496,7 @@ handle_info(T, #listener{} = S) ->
{noreply, #listener{} = l(T,S)};
handle_info(T, #monitor{} = S) ->
- m(T,S),
- x(T).
+ {noreply, #monitor{} = m(T,S)}.
%% ---------------------------------------------------------------------------
%% # code_change/3
@@ -497,6 +512,7 @@ code_change(_, State, _) ->
terminate(_, _) ->
ok.
+
%% ---------------------------------------------------------------------------
putr(Key, Val) ->
@@ -509,18 +525,47 @@ getr(Key) ->
%%
%% Transition monitor state.
+%% Outgoing message.
+m(Msg, S)
+ when is_record(Msg, diameter_packet);
+ is_binary(Msg) ->
+ send(Msg, S),
+ S;
+
+%% Transport has established a connection. Stop monitoring on the
+%% parent so as not to die before a send from the transport.
+m({start, TPid, T, Ack} = M, #monitor{transport = TPid} = S) ->
+ case T of
+ {Sock, Mod} ->
+ demonitor(S#monitor.parent, [flush]),
+ S#monitor{parent = false,
+ socket = Sock,
+ module = Mod,
+ ack = Ack};
+ false -> %% monitor not sending
+ x(M)
+ end;
+
%% Transport is telling us to die.
-m({stop, TPid}, #monitor{transport = TPid}) ->
- ok;
+m({stop, TPid} = T, #monitor{transport = TPid}) ->
+ x(T);
-%% Transport has died.
-m({'DOWN', _, process, TPid, _}, #monitor{transport = TPid}) ->
- ok;
+%% Transport is telling us to die.
+m({stop, TPid} = T, #monitor{transport = TPid}) ->
+ x(T);
-%% Transport parent has died.
-m({'DOWN', _, process, Pid, _}, #monitor{parent = Pid,
- transport = TPid}) ->
- exit(TPid, {shutdown, parent}).
+%% Transport is telling us that TLS has been negotiated after
+%% capabilities exchange.
+m({tls, SSock}, S) ->
+ S#monitor{socket = SSock,
+ module = ssl};
+
+%% Transport or parent has died.
+m({'DOWN', M, process, P, _} = T, #monitor{parent = MRef,
+ transport = TPid})
+ when M == MRef;
+ P == TPid ->
+ x(T).
%% l/2
%%
@@ -528,18 +573,16 @@ m({'DOWN', _, process, Pid, _}, #monitor{parent = Pid,
%% Service process has died.
l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid,
- socket = Sock}) ->
- gen_tcp:close(Sock),
+ socket = Sock,
+ module = M}) ->
+ M:close(Sock),
x(T);
%% Transport has been removed.
-l({transport, remove, _} = T, #listener{socket = Sock}) ->
- gen_tcp:close(Sock),
- x(T);
-
-%% Possibly death of an accepting process monitored in old code.
-l(_, S) ->
- S.
+l({transport, remove, _} = T, #listener{socket = Sock,
+ module = M}) ->
+ M:close(Sock),
+ x(T).
%% t/2
%%
@@ -557,21 +600,14 @@ t(T,S) ->
%% transition/2
-%% Incoming message.
+%% Incoming packets.
transition({P, Sock, Bin}, #transport{socket = Sock,
ssl = B,
- throttled = T}
+ frag = Frag}
= S)
when P == ssl, true == B;
P == tcp ->
- false = T, %% assert
- recv(Bin, S);
-
-%% Make a new throttling callback after a timeout.
-transition(throttle, #transport{throttled = false}) ->
- ok;
-transition(throttle, S) ->
- throttle(S);
+ recv(acc(Frag, Bin), S);
%% Capabilties exchange has decided on whether or not to run over TLS.
transition({diameter, {tls, Ref, Type, B}}, #transport{parent = Pid}
@@ -581,7 +617,7 @@ transition({diameter, {tls, Ref, Type, B}}, #transport{parent = Pid}
= NS
= tls_handshake(Type, B, S),
Pid ! {diameter, {tls, Ref}},
- throttle(NS#transport{ssl = B});
+ NS#transport{ssl = B};
transition({C, Sock}, #transport{socket = Sock,
ssl = B})
@@ -597,8 +633,18 @@ transition({E, Sock, _Reason} = T, #transport{socket = Sock,
?ERROR({T,S});
%% Outgoing message.
-transition({diameter, {send, Bin}}, S) ->
- send(Bin, S);
+transition({diameter, {send, Msg}}, #transport{} = S) ->
+ message(send, Msg, S);
+
+%% Monitor has sent an outgoing message.
+transition(Msg, S)
+ when is_record(Msg, diameter_packet);
+ is_binary(Msg) ->
+ message(ack, Msg, S);
+
+%% Deferred actions from a message_cb.
+transition({actions, Dir, Acts}, S) ->
+ setopts(actions(Acts, Dir, S));
%% Request to close the transport connection.
transition({diameter, {close, Pid}}, #transport{parent = Pid,
@@ -618,8 +664,18 @@ transition({resolve_port, Pid}, #transport{socket = Sock,
Pid ! portnr(M, Sock),
ok;
-%% Parent process has died.
-transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid}) ->
+%% Parent process has died: call the monitor to not close the socket
+%% during an ongoing send, but don't let it take forever.
+transition({'DOWN', _, process, Pid, _}, #transport{parent = Pid,
+ send = MPid}) ->
+ false == MPid
+ orelse (ok == gen_server:call(MPid, {stop, self()}, 1000))
+ orelse exit(MPid, {shutdown, parent}),
+ stop;
+
+%% Monitor process has died.
+transition({'DOWN', _, process, MPid, _}, #transport{send = MPid})
+ when is_pid(MPid) ->
stop.
%% Crash on anything unexpected.
@@ -643,11 +699,13 @@ tls_handshake(_, true, #transport{ssl = false}) ->
%% Capabilities exchange negotiated TLS: upgrade the connection.
tls_handshake(Type, true, #transport{socket = Sock,
module = M,
- ssl = Opts}
+ ssl = Opts,
+ send = MPid}
= S) ->
{ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]),
Ref = getr(?REF_KEY),
true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}}),
+ false == MPid orelse (MPid ! {tls, SSock}), %% tell the sender process
S#transport{socket = SSock,
module = ssl};
@@ -658,7 +716,7 @@ tls_handshake(_, false, S) ->
tls(connect, Sock, Opts) ->
ssl:connect(Sock, Opts);
tls(accept, Sock, Opts) ->
- ssl:ssl_accept(Sock, Opts).
+ ssl:handshake(Sock, Opts). %% assume no handshake option
%% recv/2
%%
@@ -666,92 +724,77 @@ tls(accept, Sock, Opts) ->
%% using Nagle.
%% Receive packets until a full message is received,
-recv(Bin, #transport{frag = Head, throttled = false} = S) ->
- case rcv(Head, Bin) of
- {Msg, B} ->
- throttle(S#transport{frag = B, throttled = Msg});
- Frag ->
- setopts(S),
- start_fragment_timer(S#transport{frag = Frag,
- flush = false})
- end.
-%% recv/1
+recv({Msg, Rest}, S) -> %% have a complete message ...
+ recv(acc(Rest), message(recv, Msg, S));
-recv(#transport{throttled = false} = S) ->
- recv(<<>>, S);
+recv(Frag, #transport{recv = B,
+ socket = Sock,
+ module = M}
+ = S) -> %% or not
+ B andalso setopts(M, Sock),
+ start_fragment_timer(S#transport{frag = Frag,
+ flush = false,
+ active = B}).
-recv(#transport{} = S) ->
- S.
+%% acc/2
-%% rcv/2
+%% Know how many bytes to extract.
+acc([Len | Acc], Bin) ->
+ acc1(Len, <<Acc/binary, Bin/binary>>);
-%% No previous fragment.
-rcv(<<>>, Bin) ->
- rcv(Bin);
+%% Or not.
+acc(Head, Bin) ->
+ acc(<<Head/binary, Bin/binary>>).
-%% Not even the first four bytes of the header.
-rcv(Head, Bin)
- when is_binary(Head) ->
- rcv(<<Head/binary, Bin/binary>>);
-
-%% Or enough to know how many bytes to extract.
-rcv({Len, N, Head, Acc}, Bin) ->
- rcv(Len, N + size(Bin), Head, [Bin | Acc]).
-
-%% rcv/4
+%% acc1/3
%% Extract a message for which we have all bytes.
-rcv(Len, N, Head, Acc)
- when Len =< N ->
- recv1(Len, bin(Head, Acc));
+acc1(Len, Bin)
+ when Len =< byte_size(Bin) ->
+ split_binary(Bin, Len);
%% Wait for more packets.
-rcv(Len, N, Head, Acc) ->
- {Len, N, Head, Acc}.
-
-%% rcv/1
-
-%% Nothing left.
-rcv(<<>> = Bin) ->
- Bin;
-
-%% The Message Length isn't even sufficient for a header. Chances are
-%% things will go south from here but if we're lucky then the bytes we
-%% have extend to an intended message boundary and we can recover by
-%% simply receiving them. Make it so.
-rcv(<<_:1/binary, Len:24, _/binary>> = Bin)
- when Len < 20 ->
- {Bin, <<>>};
-
-%% Enough bytes to extract a message.
-rcv(<<_:1/binary, Len:24, _/binary>> = Bin)
- when Len =< size(Bin) ->
- recv1(Len, Bin);
-
-%% Or not: wait for more packets.
-rcv(<<_:1/binary, Len:24, _/binary>> = Head) ->
- {Len, size(Head), Head, []};
+acc1(Len, Bin) ->
+ [Len | Bin].
+
+%% acc/1
+
+%% Don't match on Bin since this results in it being copied at the
+%% next append according to the Efficiency Guide. This is also the
+%% reason that the Len is extracted and maintained when accumulating
+%% messages. The simplest implementation is just to accumulate a
+%% binary and match <<_, Len:24, _/binary>> each time the length is
+%% required, but the performance of this decays quadratically with the
+%% message length, since the binary is then copied with each append of
+%% additional bytes from gen_tcp.
+
+acc(Bin)
+ when 3 < byte_size(Bin) ->
+ {Head, _} = split_binary(Bin, 4),
+ [_,A,B,C] = binary_to_list(Head),
+ Len = (A bsl 16) bor (B bsl 8) bor C,
+ if Len < 20 ->
+ %% Message length isn't sufficient for a Diameter Header.
+ %% Chances are things will go south from here but if we're
+ %% lucky then the bytes we have extend to an intended
+ %% message boundary and we can recover by simply receiving
+ %% them. Make it so.
+ {Bin, <<>>};
+ true ->
+ acc1(Len, Bin)
+ end;
%% Not even 4 bytes yet.
-rcv(Head) ->
- Head.
-
-%% recv1/2
-
-recv1(Len, Bin) ->
- <<Msg:Len/binary, Rest/binary>> = Bin,
- {Msg, Rest}.
+acc(Bin) ->
+ Bin.
-%% bin/1-2
+%% bin/1
-bin(Head, Acc) ->
- list_to_binary([Head | lists:reverse(Acc)]).
+bin([_ | Bin]) ->
+ Bin;
-bin({_, _, Head, Acc}) ->
- bin(Head, Acc);
-bin(Bin)
- when is_binary(Bin) ->
+bin(Bin) ->
Bin.
%% flush/1
@@ -768,9 +811,7 @@ bin(Bin)
%% also eventually lead to watchdog failover.
%% No fragment to flush or not receiving messages.
-flush(#transport{frag = Frag, throttled = B} = S)
- when Frag == <<>>;
- B /= false ->
+flush(#transport{frag = <<>>} = S) ->
S;
%% Messages have been received since last timer expiry.
@@ -778,9 +819,8 @@ flush(#transport{flush = false} = S) ->
start_fragment_timer(S#transport{flush = true});
%% No messages since last expiry.
-flush(#transport{frag = Frag, parent = Pid} = S) ->
- diameter_peer:recv(Pid, bin(Frag)),
- S#transport{frag = <<>>}.
+flush(#transport{frag = Frag} = S) ->
+ message(recv, bin(Frag), S#transport{frag = <<>>}).
%% start_fragment_timer/1
%%
@@ -799,7 +839,7 @@ start_fragment_timer(#transport{timeout = Tmo} = S) ->
accept(ssl, LSock) ->
case ssl:transport_accept(LSock) of
{ok, Sock} ->
- {ssl:ssl_accept(Sock), Sock};
+ ssl:handshake(Sock);
{error, _} = No ->
No
end;
@@ -813,9 +853,27 @@ connect(Mod, Host, Port, Opts) ->
%% send/2
-send(Bin, #transport{socket = Sock,
- module = M}) ->
- case send(M, Sock, Bin) of
+send(Msg, #monitor{socket = Sock, module = M, transport = TPid, ack = B}) ->
+ send1(M, Sock, Msg),
+ B andalso (TPid ! Msg);
+
+send(Msg, #transport{socket = Sock, module = M, send = false} = S) ->
+ send1(M, Sock, Msg),
+ message(ack, Msg, S);
+
+%% Send from the monitor process to avoid deadlock if both the
+%% receiver and the peer were to block in send.
+send(Msg, #transport{send = Pid} = S) ->
+ Pid ! Msg,
+ S.
+
+%% send1/3
+
+send1(Mod, Sock, #diameter_packet{bin = Bin}) ->
+ send1(Mod, Sock, Bin);
+
+send1(Mod, Sock, Bin) ->
+ case send(Mod, Sock, Bin) of
ok ->
ok;
{error, Reason} ->
@@ -842,119 +900,24 @@ setopts(M, Sock, Opts) ->
%% setopts/1
-setopts(#transport{socket = Sock, module = M}) ->
- setopts(M, Sock).
+setopts(#transport{socket = Sock,
+ active = A,
+ recv = B,
+ module = M}
+ = S)
+ when B, not A ->
+ setopts(M, Sock),
+ S#transport{active = true};
+
+setopts(S) ->
+ S.
%% setopts/2
setopts(M, Sock) ->
case setopts(M, Sock, [{active, once}]) of
ok -> ok;
- X -> x({setopts, M, Sock, X}) %% possibly on peer disconnect
- end.
-
-%% throttle/1
-
-%% Still collecting packets for a complete message: keep receiving.
-throttle(#transport{throttled = false} = S) ->
- recv(S);
-
-%% Decide whether to receive another, or whether to accept a message
-%% that's been received.
-throttle(#transport{throttle_cb = F, throttled = T} = S) ->
- Res = cb(F, T),
-
- try throttle(Res, S) of
- #transport{ssl = SB} = NS when is_boolean(SB) ->
- throttle(defrag(NS));
- #transport{throttled = Msg} = NS when is_binary(Msg) ->
- %% Initial incoming message when we might need to upgrade
- %% to TLS: wait for reception of a tls tuple.
- defrag(NS)
- catch
- #transport{} = NS ->
- recv(NS)
- end.
-
-%% cb/2
-
-cb(false, _) ->
- ok;
-
-cb(F, B) ->
- diameter_lib:eval([F, true /= B andalso B]).
-
-%% throttle/2
-
-%% Callback says to receive another message.
-throttle(ok, #transport{throttled = true} = S) ->
- throw(S#transport{throttled = false});
-
-%% Callback says to accept a received message.
-throttle(ok, #transport{parent = Pid, throttled = Msg} = S)
- when is_binary(Msg) ->
- diameter_peer:recv(Pid, Msg),
- S;
-
-throttle({ok = T, F}, S) ->
- throttle(T, S#transport{throttle_cb = F});
-
-%% Callback says to accept a received message and acknowledged the
-%% returned pid with a {request, Pid} message if a request pid is
-%% spawned, a discard message otherwise. The latter does not mean that
-%% the message was necessarily discarded: it could have been an
-%% answer.
-throttle(NPid, #transport{parent = Pid, throttled = Msg} = S)
- when is_pid(NPid), is_binary(Msg) ->
- diameter_peer:recv(Pid, {Msg, NPid}),
- S;
-
-throttle({NPid, F}, #transport{throttled = Msg} = S)
- when is_pid(NPid), is_binary(Msg) ->
- throttle(NPid, S#transport{throttle_cb = F});
-
-%% Callback to accept a received message says to discard it.
-throttle(discard, #transport{throttled = Msg} = S)
- when is_binary(Msg) ->
- S;
-
-throttle({discard = T, F}, #transport{throttled = Msg} = S)
- when is_binary(Msg) ->
- throttle(T, S#transport{throttle_cb = F});
-
-%% Callback to accept a received message says to answer it with the
-%% supplied binary.
-throttle(Bin, #transport{throttled = Msg} = S)
- when is_binary(Bin), is_binary(Msg) ->
- send(Bin, S),
- S;
-
-throttle({Bin, F}, #transport{throttled = Msg} = S)
- when is_binary(Bin), is_binary(Msg) ->
- throttle(Bin, S#transport{throttle_cb = F});
-
-%% Callback says to ask again in the specified number of milliseconds.
-throttle({timeout, Tmo}, S) ->
- erlang:send_after(Tmo, self(), throttle),
- throw(S);
-
-throttle({timeout = T, Tmo, F}, S) ->
- throttle({T, Tmo}, S#transport{throttle_cb = F});
-
-throttle(T, #transport{throttle_cb = F}) ->
- ?ERROR({invalid_return, T, F}).
-
-%% defrag/1
-%%
-%% Try to extract another message from packets already read before
-%% another throttling callback.
-
-defrag(#transport{frag = Head} = S) ->
- case rcv(Head, <<>>) of
- {Msg, B} ->
- S#transport{throttled = Msg, frag = B};
- _ ->
- S#transport{throttled = true}
+ X -> x({setopts, Sock, M, X}) %% possibly on peer disconnect
end.
%% portnr/2
@@ -990,3 +953,80 @@ getstat(gen_tcp, Sock) ->
getstat(M, Sock) ->
M:getstat(Sock).
%% Note that ssl:getstat/1 doesn't yet exist in R15B01.
+
+%% A message_cb is invoked whenever a message is sent or received, or
+%% to provide acknowledgement of a completed send or discarded
+%% request. Ignoring possible extra arguments, calls are of the
+%% following form.
+%%
+%% cb(recv, Msg) Receive a message into diameter?
+%% cb(send, Msg) Send a message on the socket?
+%% cb(ack, Msg) Acknowledgement of a completed send.
+%% cb(ack, false) Acknowledgement of a discarded request.
+%%
+%% Msg will be binary() in a recv callback, but can be a
+%% diameter_packet record in a send/ack callback if a recv/send
+%% callback returns a record. Callbacks return a list of the following
+%% form.
+%%
+%% [boolean() | send | recv | binary() | #diameter_packet{}]
+%%
+%% The atoms are meaningless by themselves, but say whether subsequent
+%% messages are to be sent or received. A boolean says whether or not
+%% to continue reading on the socket. Messages can be received even
+%% after false is returned if these arrived in the same packet. A
+%% leading recv or send is implicit on the corresponding callbacks. A
+%% new callback can be returned as the tail of a returned list: any
+%% value not of the aforementioned list type is interpreted as a
+%% callback.
+
+%% message/3
+
+message(send, false = M, S) ->
+ message(ack, M, S);
+
+message(ack, _, #transport{message_cb = false} = S) ->
+ S;
+
+message(Dir, Msg, #transport{message_cb = CB} = S) ->
+ setopts(actions(cb(CB, Dir, Msg), Dir, S)).
+
+%% actions/3
+
+actions([], _, S) ->
+ S;
+
+actions([B | As], Dir, S)
+ when is_boolean(B) ->
+ actions(As, Dir, S#transport{recv = B});
+
+actions([Dir | As], _, S)
+ when Dir == send;
+ Dir == recv ->
+ actions(As, Dir, S);
+
+actions([Msg | As], send = Dir, S)
+ when is_binary(Msg);
+ is_record(Msg, diameter_packet) ->
+ actions(As, Dir, send(Msg, S));
+
+actions([Msg | As], recv = Dir, #transport{parent = Pid} = S)
+ when is_binary(Msg);
+ is_record(Msg, diameter_packet) ->
+ diameter_peer:recv(Pid, Msg),
+ actions(As, Dir, S);
+
+actions([{defer, Tmo, Acts} | As], Dir, S) ->
+ erlang:send_after(Tmo, self(), {actions, Dir, Acts}),
+ actions(As, Dir, S);
+
+actions(CB, _, S) ->
+ S#transport{message_cb = CB}.
+
+%% cb/3
+
+cb(false, _, Msg) ->
+ [Msg];
+
+cb(CB, Dir, Msg) ->
+ diameter_lib:eval([CB, Dir, Msg]).
diff --git a/lib/diameter/test/diameter_app_SUITE.erl b/lib/diameter/test/diameter_app_SUITE.erl
index 71256020f5..ef4a28d3f4 100644
--- a/lib/diameter/test/diameter_app_SUITE.erl
+++ b/lib/diameter/test/diameter_app_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -208,9 +208,9 @@ xref(Config) ->
CTmods = CTmods -- Mods,
%% Ensure that runtime modules only call other runtime modules, or
- %% applications declared as in runtime_dependencies in the app
- %% file. Note that the declared application versions are ignored
- %% since we only know what we can see now.
+ %% applications declared in runtime_dependencies in the app file.
+ %% The declared application versions are ignored since we only
+ %% know what we see now.
[] = lists:filter(fun(M) -> not lists:member(app(M), Deps) end,
RTdeps -- Mods).
@@ -261,8 +261,12 @@ app(Mod) ->
case code:which(Mod) of
preloaded ->
"erts";
+ Reason when is_atom(Reason) ->
+ error({Reason, Mod});
Path ->
- unversion(lists:nth(3, lists:reverse(filename:split(Path))))
+ %% match to identify an unexpectedly short path
+ {_, _, [_,_,_|_] = Split} = {Mod, Path, filename:split(Path)},
+ unversion(lists:nth(3, lists:reverse(Split)))
end.
add_application(XRef, App) ->
diff --git a/lib/diameter/test/diameter_capx_SUITE.erl b/lib/diameter/test/diameter_capx_SUITE.erl
index ed6641b9fb..51b6c1d7f2 100644
--- a/lib/diameter/test/diameter_capx_SUITE.erl
+++ b/lib/diameter/test/diameter_capx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -384,7 +384,7 @@ load_dict(N) ->
A3 = erl_anno:new(3),
A4 = erl_anno:new(4),
Forms = [{attribute, A1, module, Mod},
- {attribute, A2, compile, [export_all]},
+ {attribute, A2, export, [{id,0}]},
{function, A3, id, 0,
[{clause, A4, [], [], [{integer, A4, N}]}]}],
{ok, Mod, Bin, []} = compile:forms(Forms, [return]),
@@ -433,7 +433,7 @@ server_reject(Config, F, RC) ->
?fail({LRef, OH})
end.
-%% cliient_closed/4
+%% client_closed/4
client_closed(Config, Host, F, RC) ->
true = diameter:subscribe(?CLIENT),
diff --git a/lib/diameter/test/diameter_codec_SUITE.erl b/lib/diameter/test/diameter_codec_SUITE.erl
index 558ba3b848..17112794e4 100644
--- a/lib/diameter/test/diameter_codec_SUITE.erl
+++ b/lib/diameter/test/diameter_codec_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -31,6 +31,8 @@
-export([suite/0,
all/0,
groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
init_per_group/2,
end_per_group/2,
init_per_testcase/2,
@@ -63,6 +65,12 @@ groups() ->
grouped_error,
failed_error]}].
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
init_per_group(recode, Config) ->
ok = diameter:start(),
Config.
@@ -277,7 +285,15 @@ recode(Msg) ->
recode(Msg, diameter_gen_base_rfc6733).
recode(#diameter_packet{} = Pkt, Dict) ->
- diameter_codec:decode(Dict, diameter_codec:encode(Dict, Pkt));
+ diameter_codec:decode(Dict, opts(Dict), diameter_codec:encode(Dict, Pkt));
recode(Msg, Dict) ->
recode(#diameter_packet{msg = Msg}, Dict).
+
+opts(Mod) ->
+ #{app_dictionary => Mod,
+ decode_format => record,
+ string_decode => false,
+ strict_mbit => true,
+ rfc => 6733,
+ failed_avp => false}.
diff --git a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
index 50cc6e7eef..c6bba75f09 100644
--- a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
+++ b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ enc(M, #diameter_packet{msg = Vs} = P) ->
P#diameter_packet{msg = [M|Vs]}).
run(M, Pkt) ->
- dec(M, diameter_codec:decode(diameter_test_recv, Pkt)).
+ dec(M, diameter_codec:decode(diameter_test_recv, opts(M), Pkt)).
%% Note that the recv dictionary defines neither XXX nor YYY.
dec('AR', #diameter_packet
@@ -75,3 +75,11 @@ dec('BR', #diameter_packet
errors = [{5001, ?MANDATORY_XXX},
{5008, ?NOT_MANDATORY_YYY}]}) ->
ok.
+
+opts(Mod) ->
+ #{app_dictionary => Mod,
+ decode_format => record,
+ string_decode => true,
+ strict_mbit => true,
+ rfc => 6733,
+ failed_avp => false}.
diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl
index 869797f11f..70e910ffa6 100644
--- a/lib/diameter/test/diameter_codec_test.erl
+++ b/lib/diameter/test/diameter_codec_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -44,7 +44,8 @@ base() ->
[] = run([[fun base/1, T] || T <- [zero, decode]]).
gen(Mod) ->
- Fs = [{Mod, F, []} || F <- [name, id, vendor_id, vendor_name]],
+ Fs = [{Mod, F, []} || Mod /= diameter_gen_doic_rfc7683,
+ F <- [name, id, vendor_id, vendor_name]],
[] = run(Fs ++ [[fun gen/2, Mod, T] || T <- [messages,
command_codes,
avp_types,
@@ -94,7 +95,7 @@ base(T) ->
%% Ensure that 'zero' values encode only zeros.
base(zero = T, F) ->
- B = diameter_types:F(encode, T),
+ B = diameter_types:F(encode, T, opts()),
B = z(B);
%% Ensure that we can decode what we encode and vice-versa, and that
@@ -106,7 +107,7 @@ base(decode, F) ->
[] = run([[fun base_invalid/2, F, V] || V <- Is]).
base_decode(F, Eq, Value) ->
- d(fun(X,V) -> diameter_types:F(X,V) end, Eq, Value).
+ d(fun(X,V) -> diameter_types:F(X, V, opts()) end, Eq, Value).
base_invalid(F, Value) ->
try
@@ -171,7 +172,7 @@ gen(M, avp_types, {Name, Code, Type, _Flags}) ->
V = undefined /= VendorId,
V = 0 /= Flags band 2#10000000,
{Name, Type} = M:avp_name(Code, VendorId),
- B = M:empty_value(Name),
+ B = M:empty_value(Name, #{module => M}),
B = z(B),
[] = avp_decode(M, Type, Name);
@@ -207,10 +208,23 @@ avp_decode(Mod, Name, Type, Eq, Value) ->
d(fun(X,V) -> avp(Mod, X, V, Name, Type) end, Eq, Value).
avp(Mod, decode = X, V, Name, 'Grouped') ->
- {Rec, _} = Mod:avp(X, V, Name),
+ {Rec, _} = Mod:avp(X, V, Name, opts(Mod)),
Rec;
-avp(Mod, X, V, Name, _) ->
- Mod:avp(X, V, Name).
+avp(Mod, decode = X, V, Name, _) ->
+ Mod:avp(X, V, Name, opts(Mod));
+avp(Mod, encode = X, V, Name, _) ->
+ iolist_to_binary(Mod:avp(X, V, Name, opts(Mod))).
+
+opts(Mod) ->
+ (opts())#{module => Mod,
+ app_dictionary => Mod}.
+
+opts() ->
+ #{decode_format => record,
+ string_decode => true,
+ strict_mbit => true,
+ rfc => 6733,
+ failed_avp => false}.
%% v/1
@@ -257,8 +271,8 @@ arity(M, Name, AvpName, Rec) ->
enum(M, Name, {_,E}) ->
B = <<E:32>>,
- B = M:avp(encode, E, Name),
- E = M:avp(decode, B, Name).
+ B = M:avp(encode, E, Name, opts(M)),
+ E = M:avp(decode, B, Name, opts(M)).
retag(import_avps) -> avp_types;
retag(import_groups) -> grouped;
@@ -280,7 +294,8 @@ d(F, Eq, V) ->
end.
z(B) ->
- << <<0>> || <<_>> <= B >>.
+ Sz = size(B),
+ <<0:Sz/unit:8>>.
%% values/1
%%
diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl
index 7a9ac65ae3..73fe1ef6e0 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-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@
-export([dict/0]). %% fake dictionary module
%% dictionary callbacks for flatten2/1
--export(['A1'/3, 'Unsigned32'/3]).
+-export(['A1'/4, 'Unsigned32'/4]).
-define(base, "base_rfc3588.dia").
-define(util, diameter_util).
@@ -552,13 +552,13 @@ flatten2(_Config) ->
T <- [encode, decode],
M <- [M2, M3],
Ref <- [make_ref()],
- RC <- [M:avp(T, Ref, A)],
+ RC <- [M:avp(T, Ref, A, #{module => M})],
RC /= {T, Ref}].
-'A1'(T, 'Unsigned32', Ref) ->
+'A1'(T, 'Unsigned32', Ref, _Opts) ->
{T, Ref}.
-'Unsigned32'(T, 'A3', Ref) ->
+'Unsigned32'(T, 'A3', Ref, _Opts) ->
{T, Ref}.
load_forms(Forms) ->
diff --git a/lib/diameter/test/diameter_dict_SUITE.erl b/lib/diameter/test/diameter_dict_SUITE.erl
deleted file mode 100644
index 4c1349f4eb..0000000000
--- a/lib/diameter/test/diameter_dict_SUITE.erl
+++ /dev/null
@@ -1,145 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%% Tests of the dict-like diameter_dict.
-%%
-
--module(diameter_dict_SUITE).
-
--export([suite/0,
- all/0,
- groups/0]).
-
-%% testcases
--export([append/1,
- fetch/1,
- fetch_keys/1,
- filter/1,
- find/1,
- fold/1,
- is_key/1,
- map/1,
- merge/1,
- update/1,
- update_counter/1]).
-
--include("diameter_ct.hrl").
-
--define(dict, diameter_dict).
--define(util, diameter_util).
-
-%% ===========================================================================
-
-suite() ->
- [{timetrap, {seconds, 60}}].
-
-all() ->
- [{group, all},
- {group, all, [parallel]}].
-
-groups() ->
- [{all, [], tc()}].
-
-tc() ->
- [append,
- fetch,
- fetch_keys,
- filter,
- find,
- fold,
- is_key,
- map,
- merge,
- update,
- update_counter].
-
-%% ===========================================================================
-
--define(KV100, [{N,[N]} || N <- lists:seq(1,100)]).
-
-append(_) ->
- D = ?dict:append(k, v, ?dict:new()),
- [{k,[v,v]}] = ?dict:to_list(?dict:append(k, v, D)).
-
-fetch(_) ->
- D = ?dict:from_list(?KV100),
- [50] = ?dict:fetch(50, D),
- Ref = make_ref(),
- Ref = try ?dict:fetch(Ref, D) catch _:_ -> Ref end.
-
-fetch_keys(_) ->
- L = ?KV100,
- D = ?dict:from_list(L),
- L = [{N,[N]} || N <- lists:sort(?dict:fetch_keys(D))].
-
-filter(_) ->
- L = ?KV100,
- F = fun(K,[_]) -> 0 == K rem 2 end,
- D = ?dict:filter(F, ?dict:from_list(L)),
- true = [T || {K,V} = T <- L, F(K,V)] == lists:sort(?dict:to_list(D)).
-
-find(_) ->
- D = ?dict:from_list(?KV100),
- {ok, [50]} = ?dict:find(50, D),
- error = ?dict:find(make_ref(), D).
-
-fold(_) ->
- L = ?KV100,
- S = lists:sum([N || {N,_} <- L]),
- S = ?dict:fold(fun(K,[_],A) -> K + A end, 0, ?dict:from_list(L)).
-
-is_key(_) ->
- L = ?KV100,
- D = ?dict:from_list(L),
- true = lists:all(fun({N,_}) -> ?dict:is_key(N,D) end, L),
- false = ?dict:is_key(make_ref(), D).
-
-map(_) ->
- L = ?KV100,
- F = fun(_,V) -> [N] = V, N*2 end,
- D = ?dict:map(F, ?dict:from_list(L)),
- M = [{K, F(K,V)} || {K,V} <- L],
- M = lists:sort(?dict:to_list(D)).
-
-merge(_) ->
- L = ?KV100,
- F = fun(_,V1,V2) -> V1 ++ V2 end,
- D = ?dict:merge(F, ?dict:from_list(L), ?dict:from_list(L)),
- M = [{K, F(K,V,V)} || {K,V} <- L],
- M = lists:sort(?dict:to_list(D)).
-
-update(_) ->
- L = ?KV100,
- F = fun([V]) -> 2*V end,
- D = ?dict:update(50, F, ?dict:from_list(L)),
- 100 = ?dict:fetch(50, D),
- Ref = make_ref(),
- Ref = try ?dict:update(Ref, F, D) catch _:_ -> Ref end,
- [Ref] = ?dict:fetch(Ref, ?dict:update(Ref,
- fun(_,_) -> ?ERROR(i_think_not) end,
- [Ref],
- D)).
-
-update_counter(_) ->
- L = [{N,2*N} || {N,_} <- ?KV100],
- D = ?dict:update_counter(50, 20, ?dict:from_list(L)),
- 120 = ?dict:fetch(50,D),
- 2 = ?dict:fetch(1,D).
diff --git a/lib/diameter/test/diameter_dpr_SUITE.erl b/lib/diameter/test/diameter_dpr_SUITE.erl
index 55702fbf78..779b919d3c 100644
--- a/lib/diameter/test/diameter_dpr_SUITE.erl
+++ b/lib/diameter/test/diameter_dpr_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -27,6 +27,8 @@
-export([suite/0,
all/0,
groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
init_per_group/2,
end_per_group/2]).
@@ -56,16 +58,12 @@
%% Config for diameter:start_service/2.
-define(SERVICE(Host),
- [{'Origin-Host', Host},
+ [{'Origin-Host', Host ++ ".erlang.org"},
{'Origin-Realm', "erlang.org"},
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', hd(Host)}, %% match this in disconnect/5
{'Product-Name', "OTP/diameter"},
- {'Acct-Application-Id', [0]},
- {restrict_connections, false},
- {application, [{dictionary, diameter_gen_base_rfc6733},
- {alias, common},
- {module, #diameter_callback{_ = false}}]}]).
+ {restrict_connections, false}]).
%% Disconnect reasons that diameter passes as the first argument of a
%% function configured as disconnect_cb.
@@ -89,13 +87,19 @@ suite() ->
[{timetrap, {seconds, 60}}].
all() ->
- [start, send_dpr, stop | [{group, R} || R <- ?REASONS]].
+ [{group, R} || R <- [client, server, uncommon | ?REASONS]].
%% The group determines how transports are terminated: by remove_transport,
%% stop_service or application stop.
groups() ->
- Ts = tc(),
- [{R, [], Ts} || R <- ?REASONS].
+ [{R, [], [start, send_dpr, stop]} || R <- [client, server, uncommon]]
+ ++ [{R, [], Ts} || Ts <- [tc()], R <- ?REASONS].
+
+init_per_suite(Config) -> %% not need, but a useful place to enable trace
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
init_per_group(Name, Config) ->
[{group, Name} | Config].
@@ -107,29 +111,86 @@ tc() ->
[start, connect, remove_transport, stop_service, check, stop].
%% ===========================================================================
-%% start/stop testcases
-start(_Config) ->
- ok = diameter:start(),
- ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER)),
- ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT)).
+%% start/1
-send_dpr(_Config) ->
+start(Config)
+ when is_list(Config) ->
+ Grp = group(Config),
+ ok = diameter:start(),
+ ok = diameter:start_service(?SERVER, service(?SERVER, Grp)),
+ ok = diameter:start_service(?CLIENT, service(?CLIENT, Grp)).
+
+service(?SERVER = Svc, _) ->
+ ?SERVICE(Svc)
+ ++ [{'Acct-Application-Id', [0,3]},
+ {application, [{dictionary, diameter_gen_base_rfc6733},
+ {alias, common},
+ {module, #diameter_callback{_ = false}}]},
+ {application, [{dictionary, diameter_gen_acct_rfc6733},
+ {alias, acct},
+ {module, #diameter_callback{_ = false}}]}];
+
+%% Client that receives a server DPR despite no explicit support for
+%% Diameter common messages.
+service(?CLIENT = Svc, server) ->
+ ?SERVICE(Svc)
+ ++ [{'Acct-Application-Id', [3]},
+ {application, [{dictionary, diameter_gen_acct_rfc6733},
+ {alias, acct},
+ {module, #diameter_callback{_ = false}}]}];
+
+%% Client that sends DPR despite advertised only the accounting
+%% application. The dictionary is required for encode.
+service(?CLIENT = Svc, uncommon) ->
+ ?SERVICE(Svc)
+ ++ [{'Acct-Application-Id', [3]},
+ {application, [{dictionary, diameter_gen_base_rfc6733},
+ {alias, common},
+ {module, #diameter_callback{_ = false}}]},
+ {application, [{dictionary, diameter_gen_acct_rfc6733},
+ {alias, acct},
+ {module, #diameter_callback{_ = false}}]}];
+
+service(?CLIENT = Svc, _) ->
+ ?SERVICE(Svc)
+ ++ [{'Auth-Application-Id', [0]},
+ {application, [{dictionary, diameter_gen_base_rfc6733},
+ {alias, common},
+ {module, #diameter_callback{_ = false}}]}].
+
+%% send_dpr/1
+
+send_dpr(Config) ->
LRef = ?util:listen(?SERVER, tcp),
Ref = ?util:connect(?CLIENT, tcp, LRef, [{dpa_timeout, 10000}]),
+ Svc = sender(group(Config)),
+ [Info] = diameter:service_info(Svc, connections),
+ {_, {TPid, _}} = lists:keyfind(peer, 1, Info),
#diameter_base_DPA{'Result-Code' = 2001}
- = diameter:call(?CLIENT,
+ = diameter:call(Svc,
common,
- ['DPR', {'Origin-Host', "CLIENT.erlang.org"},
- {'Origin-Realm', "erlang.org"},
- {'Disconnect-Cause', 0}]),
- ok = receive %% endure the transport dies on DPA
+ ['DPR', {'Origin-Host', Svc ++ ".erlang.org"},
+ {'Origin-Realm', "erlang.org"},
+ {'Disconnect-Cause', 0}],
+ [{peer, TPid}]),
+ ok = receive %% ensure the transport dies on DPA
#diameter_event{service = ?CLIENT, info = {down, Ref, _, _}} ->
ok
after 5000 ->
erlang:process_info(self(), messages)
end.
+%% sender/1
+
+sender(server) ->
+ ?SERVER;
+
+sender(_) ->
+ ?CLIENT.
+
+%% connect/1
+
connect(Config) ->
Pid = spawn(fun init/0), %% process for disconnect_cb to bang
Grp = group(Config),
@@ -138,16 +199,22 @@ connect(Config) ->
|| RCs <- ?RETURNS],
?util:write_priv(Config, config, [Pid | Refs]).
+%% remove_transport/1
+
%% Remove all the client transports only in the transport group.
remove_transport(Config) ->
transport == group(Config)
andalso (ok = diameter:remove_transport(?CLIENT, true)).
+%% stop_service/1
+
%% Stop the service only in the service group.
stop_service(Config) ->
service == group(Config)
andalso (ok = diameter:stop_service(?CLIENT)).
+%% check/1
+
%% Check for callbacks before diameter:stop/0, not the other way around
%% for the timing reason explained below.
check(Config) ->
@@ -157,9 +224,13 @@ check(Config) ->
Dict = receive {Pid, D} -> D end, %% get it
check(Refs, ?RETURNS, Grp, Dict). %% check for callbacks
+%% stop/1
+
stop(_Config) ->
ok = diameter:stop().
+%% ===========================================================================
+
%% Whether or not there are callbacks after diameter:stop() depends on
%% timing as long as the server runs on the same node: a server
%% transport could close the connection before the client has chance
diff --git a/lib/diameter/test/diameter_event_SUITE.erl b/lib/diameter/test/diameter_event_SUITE.erl
index 57d3427037..a291dde6be 100644
--- a/lib/diameter/test/diameter_event_SUITE.erl
+++ b/lib/diameter/test/diameter_event_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -63,7 +63,8 @@
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
{'Product-Name', "OTP/diameter"},
- {'Acct-Application-Id', [D:id() || D <- Dicts]}
+ {'Acct-Application-Id', [D:id() || D <- Dicts]},
+ {decode_format, map}
| [{application, [{dictionary, D},
{module, #diameter_callback{}}]}
|| D <- Dicts]]).
@@ -111,7 +112,8 @@ up(Config) ->
{Svc, Ref} = connect(Config, [{connect_timer, 5000},
{watchdog_timer, 15000}]),
start = event(Svc),
- {up, Ref, {TPid, Caps}, Cfg, #diameter_packet{}} = event(Svc),
+ {up, Ref, {TPid, Caps}, Cfg, #diameter_packet{msg = M}} = event(Svc),
+ ['CEA' | #{}] = M, %% assert
{watchdog, Ref, _, {initial, okay}, _} = event(Svc),
%% Kill the transport process and see that the connection is
%% reestablished after a watchdog timeout, not after connect_timer
@@ -131,8 +133,9 @@ down(Config) ->
{connect_timer, 5000},
{watchdog_timer, 20000}]),
start = event(Svc),
- {closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{}}, _}
+ {closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{msg = M}}, _}
= event(Svc),
+ ['CEA' | #{}] = M, %% assert
{reconnect, Ref, _} = event(Svc, 4000, 10000).
%% Connect with matching capabilities but have the server delay its
diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl
index e4ed2b227d..ee44ed8dc9 100644
--- a/lib/diameter/test/diameter_examples_SUITE.erl
+++ b/lib/diameter/test/diameter_examples_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -92,12 +92,10 @@ init_per_group(tcp = N, Config) ->
[{group, N} | Config];
init_per_group(sctp = N, Config) ->
- case gen_sctp:open() of
- {ok, Sock} ->
- gen_sctp:close(Sock),
+ case ?util:have_sctp() of
+ true ->
[{group, N} | Config];
- {error, E} when E == eprotonosupport;
- E == esocktnosupport -> %% fail on any other reason
+ false->
{skip, no_sctp}
end.
@@ -346,7 +344,7 @@ top(Dir, LibDir) ->
start({server, Prot}) ->
ok = diameter:start(),
ok = server:start(),
- {ok, Ref} = server:listen(Prot),
+ {ok, Ref} = server:listen({Prot, any, 3868}),
[_] = ?util:lport(Prot, Ref),
ok;
@@ -354,7 +352,7 @@ start({client = Svc, Prot}) ->
ok = diameter:start(),
true = diameter:subscribe(Svc),
ok = client:start(),
- {ok, Ref} = client:connect(Prot),
+ {ok, Ref} = client:connect({Prot, loopback, loopback, 3868}),
receive #diameter_event{info = {up, Ref, _, _, _}} -> ok end;
start(Config) ->
diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
index 79db39ca45..ccee6baec1 100644
--- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl
+++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -33,8 +33,8 @@
end_per_suite/1]).
%% testcases
--export([send_not_from_controlling_process/1,
- send_from_multiple_clients/1, send_from_multiple_clients/0,
+-export([send_one_from_many/1, send_one_from_many/0,
+ send_many_from_one/1, send_many_from_one/0,
receive_what_was_sent/1]).
-include_lib("kernel/include/inet_sctp.hrl").
@@ -45,16 +45,24 @@
%% Open sockets on the loopback address.
-define(ADDR, {127,0,0,1}).
-%% Snooze, nap, siesta.
--define(SLEEP(T), receive after T -> ok end).
-
%% An indescribably long number of milliseconds after which everthing
%% that should have happened has.
-define(FOREVER, 2000).
+%% How many milliseconds to tolerate between the fastest and slowest
+%% turnaround times.
+-define(VARIANCE, 100).
+
%% The first byte in each message we send as a simple guard against
%% not receiving what was sent.
--define(MAGIC, 42).
+-define(MAGIC, 0).
+
+%% Requested number of inbound/outbound streams.
+-define(STREAMS, 5).
+
+%% Success for send_multiple. Match in each testcase rather than in
+%% send_multiple itself for a better failure in common_test.
+-define(OK, {_, true, _, [true, true], [], _}).
%% ===========================================================================
@@ -62,8 +70,8 @@ suite() ->
[{timetrap, {seconds, 10}}].
all() ->
- [send_not_from_controlling_process,
- send_from_multiple_clients,
+ [send_one_from_many,
+ send_many_from_one,
receive_what_was_sent].
init_per_suite(Config) ->
@@ -81,130 +89,37 @@ end_per_suite(_Config) ->
%% ===========================================================================
-%% send_not_from_controlling_process/1
-%%
-%% This testcase failing shows gen_sctp:send/4 hanging when called
-%% outside the controlling process of the socket in question.
-
-send_not_from_controlling_process(_) ->
- Pids = send_not_from_controlling_process(),
- ?SLEEP(?FOREVER),
- try
- [] = [{P,I} || P <- Pids, I <- [process_info(P)], I /= undefined]
- after
- lists:foreach(fun(P) -> exit(P, kill) end, Pids)
- end.
-
-%% send_not_from_controlling_process/0
-%%
-%% Returns the pids of three spawned processes: a listening process, a
-%% connecting process and a sending process.
-%%
-%% The expected behaviour is that all three processes exit:
-%%
-%% - The listening process exits upon receiving an SCTP message
-%% sent by the sending process.
-%% - The connecting process exits upon listening process exit.
-%% - The sending process exits upon gen_sctp:send/4 return.
-%%
-%% The observed behaviour is that all three processes remain alive
-%% indefinitely:
-%%
-%% - The listening process never receives the SCTP message sent
-%% by the sending process.
-%% - The connecting process has an inet_reply message in its mailbox
-%% as a consequence of the call to gen_sctp:send/4 call from the
-%% sending process.
-%% - The call to gen_sctp:send/4 in the sending process doesn't return,
-%% hanging in prim_inet:getopts/2.
-
-send_not_from_controlling_process() ->
- FPid = self(),
- {L, MRef} = spawn_monitor(fun() -> listen(FPid) end),
- receive
- {?MODULE, C, S} ->
- demonitor(MRef, [flush]),
- [L,C,S];
- {'DOWN', MRef, process, _, _} = T ->
- error(T)
- end.
-
-%% listen/1
-
-listen(FPid) ->
- {ok, Sock} = open(),
- ok = gen_sctp:listen(Sock, true),
- {ok, PortNr} = inet:port(Sock),
- LPid = self(),
- spawn(fun() -> connect1(PortNr, FPid, LPid) end), %% connecting process
- Id = assoc(Sock),
- recv(Sock, Id).
-
-%% connect1/3
-
-connect1(PortNr, FPid, LPid) ->
- {ok, Sock} = open(),
- ok = gen_sctp:connect_init(Sock, ?ADDR, PortNr, []),
- Id = assoc(Sock),
- FPid ! {?MODULE,
- self(),
- spawn(fun() -> send(Sock, Id) end)}, %% sending process
- MRef = monitor(process, LPid),
- down(MRef). %% Waits with this as current_function.
-
-%% down/1
-
-down(MRef) ->
- receive {'DOWN', MRef, process, _, Reason} -> Reason end.
-
-%% send/2
-
-send(Sock, Id) ->
- ok = gen_sctp:send(Sock, Id, 0, <<0:32>>).
-
-%% ===========================================================================
-
-%% send_from_multiple_clients/0
+%% send_one_from_many/0
%%
%% Demonstrates sluggish delivery of messages.
-send_from_multiple_clients() ->
- [{timetrap, {seconds, 60}}].
+send_one_from_many() ->
+ [{timetrap, {seconds, 30}}].
-send_from_multiple_clients(_) ->
- {S, Rs} = T = send_from_multiple_clients(8, 1024),
- Max = ?FOREVER*1000,
- {false, [], _} = {Max < S,
- Rs -- [OI || {O,_} = OI <- Rs, is_integer(O)],
- T}.
+send_one_from_many(_) ->
+ ?OK = send_multiple(128, 1, 1024).
-%% send_from_multiple_clients/2
+%% send_one_from_many/2
%%
%% Opens a listening socket and then spawns a specified number of
-%% processes, each of which connects to the listening socket. Each
-%% connecting process then sends a message, whose size in bytes is
-%% passed as an argument, the listening process sends a reply
-%% containing the time at which the message was received, and the
-%% connecting process then exits upon reception of this reply.
+%% processes, each of which connects, sends a message, receives a
+%% reply, and exits.
%%
%% Returns the elapsed time for all connecting process to exit
-%% together with a list of exit reasons for the connecting processes.
-%% In the successful case a connecting process exits with the
-%% outbound/inbound transit times for the sent/received message as
-%% reason.
+%% together with a list of exit reasons. In the successful case a
+%% connecting process exits with the outbound/inbound transit times
+%% for the sent/received message as reason.
%%
%% The observed behaviour is that some outbound messages (that is,
%% from a connecting process to the listening process) can take an
%% unexpectedly long time to complete their journey. The more
-%% connecting processes, the longer the possible delay it seems.
+%% connecting processes, the longer it can take it seems.
%%
-%% eg. (With F = fun send_from_multiple_clients/2.)
-%%
-%% 5> F(2, 1024).
+%% eg. 5> send_one_from_many(2, 1024).
%% {875,[{128,116},{113,139}]}
-%% 6> F(4, 1024).
+%% 6> send_one_from_many(4, 1024).
%% {2995290,[{2994022,250},{2994071,80},{200,130},{211,113}]}
-%% 7> F(8, 1024).
+%% 7> send_one_from_many(8, 1024).
%% {8997461,[{8996161,116},
%% {2996471,86},
%% {2996278,116},
@@ -213,7 +128,7 @@ send_from_multiple_clients(_) ->
%% {213,159},
%% {373,173},
%% {376,118}]}
-%% 8> F(8, 1024).
+%% 8> send_one_from_many(8, 1024).
%% {21001891,[{20999968,128},
%% {8997891,172},
%% {8997927,91},
@@ -223,120 +138,279 @@ send_from_multiple_clients(_) ->
%% {117,98},
%% {149,125}]}
%%
-%% This turns out to have been due to SCTP resends as a consequence of
-%% the listener having an insufficient recbuf. Increasing the size
-%% solves the problem.
-%%
-
-send_from_multiple_clients(N, Sz)
- when is_integer(N), 0 < N, is_integer(Sz), 0 < Sz ->
- timer:tc(fun listen/2, [N, <<?MAGIC, 0:Sz/unit:8>>]).
-%% listen/2
-
-listen(N, Bin) ->
+send_multiple(Clients, Msgs, Sz)
+ when is_integer(Clients), 0 < Clients,
+ is_integer(Msgs), 0 < Msgs,
+ is_integer(Sz), 0 < Sz ->
+ T0 = diameter_lib:now(),
+ {S, Res} = timer:tc(fun listen/3, [Clients, Msgs, Sz]),
+ report(T0, Res),
+ Ts = lists:append(Res),
+ Outgoing = [DT || {_,{_,_,DT},{_,_,_},_} <- Ts],
+ Incoming = [DT || {_,{_,_,_},{_,_,DT},_} <- Ts],
+ Diffs = [lists:max(L) - lists:min(L) || L <- [Outgoing, Incoming]],
+ {S,
+ S < ?FOREVER*1000,
+ Diffs,
+ [D < V || V <- [?VARIANCE*1000], D <- Diffs],
+ [T || T <- Ts, [] == [T || {_,{_,_,_},{_,_,_},_} <- [T]]],
+ Res}.
+
+%% listen/3
+
+listen(Clients, Msgs, Sz) ->
{ok, Sock} = open(),
ok = gen_sctp:listen(Sock, true),
{ok, PortNr} = inet:port(Sock),
%% Spawn a middleman that in turn spawns N connecting processes,
%% collects a list of exit reasons and then exits with the list as
- %% reason. loop/3 returns when we receive this list from the
+ %% reason. accept/2 returns when we receive this list from the
%% middleman's 'DOWN'.
Self = self(),
- Fun = fun() -> exit(connect2(Self, PortNr, Bin)) end,
- {_, MRef} = spawn_monitor(fun() -> exit(fold(N, Fun)) end),
- loop(Sock, MRef, Bin).
+ Fun = fun() -> exit(client(Self, PortNr, Msgs, Sz)) end, %% start clients
+ {_, MRef} = spawn_monitor(fun() -> exit(clients(Clients, Fun)) end),
+ accept_loop(Sock, MRef).
-%% fold/2
+%% fclients/2
%%
%% Spawn N processes and collect their exit reasons in a list.
-fold(N, Fun) ->
+clients(N, Fun) ->
start(N, Fun),
acc(N, []).
+%% start/2
+
start(0, _) ->
ok;
+
start(N, Fun) ->
spawn_monitor(Fun),
start(N-1, Fun).
+%% acc/2
+
acc(0, Acc) ->
Acc;
+
acc(N, Acc) ->
receive
{'DOWN', _MRef, process, _, RC} ->
acc(N-1, [RC | Acc])
end.
-%% loop/3
+%% accept_loop/2
-loop(Sock, MRef, Bin) ->
+accept_loop(Sock, MRef) ->
+ ok = inet:setopts(Sock, [{active, once}]),
receive
- ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = Id}], B})
- when is_binary(B) ->
- Sz = size(Bin),
- {Sz, Bin} = {size(B), B}, %% assert
- ok = send(Sock, Id, mark(Bin)),
- loop(Sock, MRef, Bin);
+ ?SCTP(Sock, {_, #sctp_assoc_change{state = comm_up,
+ outbound_streams = OS,
+ assoc_id = Id}}) ->
+ Self = self(),
+ TPid = spawn(fun() -> assoc(monitor(process, Self), Id, OS) end),
+ NewSock = peeloff(Sock, Id, TPid),
+ TPid ! {peeloff, NewSock},
+ accept_loop(Sock, MRef);
?SCTP(Sock, _) ->
- loop(Sock, MRef, Bin);
+ accept_loop(Sock, MRef);
{'DOWN', MRef, process, _, Reason} ->
- Reason
+ Reason;
+ T ->
+ error(T)
end.
-%% connect2/3
+%% assoc/3
+%%
+%% Server process that answers incoming messages as long as the parent
+%% lives.
+
+assoc(MRef, _Id, OS)
+ when is_reference(MRef) ->
+ {peeloff, Sock} = receive T -> T end,
+ recv_loop(Sock, false, sender(Sock, false, OS), MRef).
+
+%% recv_loop/4
+
+recv_loop(Sock, Id, Pid, MRef) ->
+ ok = inet:setopts(Sock, [{active, once}]),
+ recv(Sock, Id, Pid, MRef, receive T -> T end).
+
+%% recv/5
+
+%% Association id can change on a peeloff socket on some versions of
+%% Solaris.
+recv(Sock,
+ false,
+ Pid,
+ MRef,
+ ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = Id}], _})
+ = T) ->
+ Pid ! {assoc_id, Id},
+ recv(Sock, Id, Pid, MRef, T);
+
+recv(Sock, Id, Pid, MRef, ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = I}], B}))
+ when is_binary(B) ->
+ T2 = diameter_lib:now(),
+ Id = I, %% assert
+ <<?MAGIC, Bin/binary>> = B, %% assert
+ {[_,_,_,Sz] = L, Bytes} = unmark(Bin),
+ Sz = size(Bin) - Bytes, %% assert
+ <<_:Bytes/binary, Body:Sz/binary>> = Bin,
+ send(Pid, [T2|L], Body), %% answer
+ recv_loop(Sock, Id, Pid, MRef);
+
+recv(Sock, Id, Pid, MRef, ?SCTP(Sock, _)) ->
+ recv_loop(Sock, Id, Pid, MRef);
+
+recv(_, _, _, MRef, {'DOWN', MRef, process, _, Reason}) ->
+ Reason;
+
+recv(_, _, _, _, T) ->
+ error(T).
-connect2(Pid, PortNr, Bin) ->
- monitor(process, Pid),
+%% send/3
- {ok, Sock} = open(),
- ok = gen_sctp:connect_init(Sock, ?ADDR, PortNr, []),
- Id = assoc(Sock),
+send(Pid, Header, Body) ->
+ Pid ! {send, Header, Body}.
- %% T1 = time before send
- %% T2 = time after listening process received our message
- %% T3 = time after reply is received
+%% sender/3
+%%
+%% Start a process that sends, so as not to block the controlling process.
- T1 = diameter_lib:now(),
- ok = send(Sock, Id, Bin),
- T2 = unmark(recv(Sock, Id)),
- T3 = diameter_lib:now(),
- {diameter_lib:micro_diff(T2, T1), %% Outbound
- diameter_lib:micro_diff(T3, T2)}. %% Inbound
+sender(Sock, Id, OS) ->
+ Pid = self(),
+ spawn(fun() -> send_loop(Sock, Id, OS, 1, monitor(process, Pid)) end).
-%% recv/2
+%% send_loop/5
-recv(Sock, Id) ->
+send_loop(Sock, Id, OS, N, MRef) ->
receive
- ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = I}], Bin})
- when is_binary(Bin) ->
- Id = I, %% assert
- Bin;
- ?SCTP(S, _) ->
- Sock = S, %% assert
- recv(Sock, Id);
+ {assoc_id, I} ->
+ send_loop(Sock, I, OS, N, MRef);
+ {send, L, Body} ->
+ Stream = N rem OS,
+ ok = send(Sock, Id, Stream, mark(Body, [N, Stream | L])),
+ send_loop(Sock, Id, OS, N+1, MRef);
+ {'DOWN', MRef, process, _, _} = T ->
+ T;
T ->
- exit(T)
+ error(T)
end.
-%% send/3
+%% peeloff/3
+
+peeloff(LSock, Id, TPid) ->
+ {ok, Sock} = gen_sctp:peeloff(LSock, Id),
+ ok = gen_sctp:controlling_process(Sock, TPid),
+ Sock.
+
+%% client/4
+
+client(Pid, PortNr, Msgs, Sz) ->
+ monitor(process, Pid),
+ {ok, Sock} = open(),
+ ok = gen_sctp:connect_init(Sock, ?ADDR, PortNr, []),
+ recv_loop(Sock, Msgs, Sz).
-send(Sock, Id, Bin) ->
- gen_sctp:send(Sock, Id, 0, Bin).
+%% recv_loop/3
-%% mark/1
+recv_loop(_, 0, T) ->
+ [_,_|Acc] = T,
+ Acc;
+
+recv_loop(Sock, Msgs, T) ->
+ ok = inet:setopts(Sock, [{active, once}]),
+ {I, NewT} = recv(Sock, Msgs, T, receive X -> X end),
+ recv_loop(Sock, Msgs - I, NewT).
+
+%% recv/4
+
+recv(Sock, Msgs, Sz, ?SCTP(Sock, {_, #sctp_assoc_change{} = A})) ->
+ #sctp_assoc_change{state = comm_up, %% assert
+ assoc_id = Id,
+ outbound_streams = OS}
+ = A,
+ true = is_integer(Sz), %% assert
+ send_n(Msgs, sender(Sock, Id, OS), Sz),
+ {0, [Id, OS]};
+
+recv(Sock, _, T, ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = Id}], Bin})) ->
+ T4 = diameter_lib:now(),
+ [Id, OS | Acc] = T,
+ {1, [Id, OS, stat(T4, Bin) | Acc]};
+
+recv(Sock, _, T, ?SCTP(Sock, _)) ->
+ {0, [_,_|_] = T};
+
+recv(_, _, _, T) ->
+ error(T).
+
+%% send_n/3
+%%
+%% Send messages to the server from dedicated processes.
+
+send_n(0, _, _) ->
+ ok;
-mark(Bin) ->
- Info = term_to_binary(diameter_lib:now()),
+send_n(N, Pid, Sz) ->
+ M = rand:uniform(255),
+ send(Pid, [Sz], binary:copy(<<M>>, Sz)),
+ send_n(N-1, Pid, Sz).
+
+%% send/4
+
+send(Sock, Id, Stream, Bin) ->
+ case gen_sctp:send(Sock, Id, Stream, <<?MAGIC, Bin/binary>>) of
+ {error, eagain} ->
+ send(Sock, Id, Stream, Bin);
+ RC ->
+ RC
+ end.
+
+%% stat/2
+
+stat(T4, <<?MAGIC, Bin/binary>>) ->
+ %% T1 = time at send
+ %% T2 = time at reception by server
+ %% T3 = time at reception by server's sender
+ %% T4 = time at reception of answer
+
+ {[T3,NI,SI,T2,T1,NO,SO,Sz], Bytes} = unmark(Bin),
+
+ Sz = size(Bin) - Bytes, %% assert
+
+ {T1,
+ {NO, SO, diameter_lib:micro_diff(T2, T1)}, %% Outbound
+ {NI, SI, diameter_lib:micro_diff(T4, T3)}, %% Inbound
+ T4}.
+
+%% mark/2
+
+mark(Bin, T) ->
+ Info = term_to_binary([diameter_lib:now() | T]),
<<Info/binary, Bin/binary>>.
%% unmark/1
unmark(Bin) ->
- binary_to_term(Bin).
+ T = binary_to_term(Bin),
+ {T, size(term_to_binary(T))}.
+
+%% ===========================================================================
+
+%% send_many_from_one/0
+%%
+%% Demonstrates sluggish delivery of messages.
+
+send_many_from_one() ->
+ [{timetrap, {seconds, 30}}].
+
+send_many_from_one(_) ->
+ ?OK = send_multiple(1, 128, 1024).
%% ===========================================================================
@@ -345,7 +419,7 @@ unmark(Bin) ->
%% Demonstrates reception of a message that differs from that sent.
receive_what_was_sent(_Config) ->
- send_from_multiple_clients(1, 1024*32). %% fails
+ ?OK = send_multiple(1, 1, 1024*32).
%% ===========================================================================
@@ -357,16 +431,23 @@ open() ->
%% open/1
open(Opts) ->
- gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, true}, binary,
+ gen_sctp:open([{ip, ?ADDR}, {port, 0}, {active, false}, binary,
+ {sctp_initmsg, #sctp_initmsg{num_ostreams = ?STREAMS,
+ max_instreams = ?STREAMS}},
{recbuf, 1 bsl 16}, {sndbuf, 1 bsl 16}
| Opts]).
-%% assoc/1
+%% report/2
-assoc(Sock) ->
- receive
- ?SCTP(Sock, {_, #sctp_assoc_change{state = S,
- assoc_id = Id}}) ->
- comm_up = S, %% assert
- Id
- end.
+report(T0, Ts) ->
+ ct:pal("~p~n", [lists:sort([sort([{diameter_lib:micro_diff(T1,T0),
+ OT,
+ IT,
+ diameter_lib:micro_diff(T4,T0)}
+ || {T1,OT,IT,T4} <- L])
+ || L <- Ts])]).
+
+%% sort/1
+
+sort(L) ->
+ lists:sort(fun({_,{N,_,_},_,_}, {_,{M,_,_},_,_}) -> N =< M end, L).
diff --git a/lib/diameter/test/diameter_gen_tcp_SUITE.erl b/lib/diameter/test/diameter_gen_tcp_SUITE.erl
index 2be2cf4b35..db42ea813e 100644
--- a/lib/diameter/test/diameter_gen_tcp_SUITE.erl
+++ b/lib/diameter/test/diameter_gen_tcp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2014-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2014-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ all() ->
send_long(_) ->
{Sock, SendF} = connection(),
- B = list_to_binary(lists:duplicate(1 bsl 20, $X)),
+ B = binary:copy(<<$X>>, 1 bsl 20),
ok = SendF(B),
B = recv(Sock, size(B), []).
diff --git a/lib/diameter/test/diameter_pool_SUITE.erl b/lib/diameter/test/diameter_pool_SUITE.erl
index eadb354a1d..97c16940ff 100644
--- a/lib/diameter/test/diameter_pool_SUITE.erl
+++ b/lib/diameter/test/diameter_pool_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2015. All Rights Reserved.
+%% Copyright Ericsson AB 2015-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -115,7 +115,7 @@ connect(ClientProt, ServerProt) ->
%% 'up' events. (Although it's likely.)
sleep(),
{9,5} = count("server", LRef, accept), %% 5 connections + 4 accepting
- %% Ensure ther are still the expected number of accepting transports
+ %% Ensure there are still the expected number of accepting transports
%% after stopping the client service.
ok = diameter:stop_service("client"),
sleep(),
diff --git a/lib/diameter/test/diameter_reg_SUITE.erl b/lib/diameter/test/diameter_reg_SUITE.erl
index e2a1ca00c3..cd9242faa8 100644
--- a/lib/diameter/test/diameter_reg_SUITE.erl
+++ b/lib/diameter/test/diameter_reg_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
-export([add/1,
add_new/1,
remove/1,
+ down/1,
terms/1,
pids/1]).
@@ -56,6 +57,7 @@ tc() ->
[add,
add_new,
remove,
+ down,
terms,
pids].
@@ -88,6 +90,13 @@ remove(_) ->
[{Ref, Pid}] = ?reg:match(Ref),
Pid = self().
+down(_) ->
+ Ref = make_ref(),
+ {_, MRef} = spawn_monitor(fun() -> ?reg:add_new(Ref), timer:sleep(1000) end),
+ receive {'DOWN', MRef, process, _, _} -> ok end,
+ timer:sleep(1000),
+ [] = ?reg:match(Ref).
+
terms(_) ->
Ref = make_ref(),
true = ?reg:add_new(Ref),
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index 5353688bf4..9de5cbe685 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -302,7 +302,7 @@ stats(?RELAY1, L) ->
%% RAR x 2 (send_timeout_[12])
{{{0,257,0},recv},3}, %% CEA
{{{0,257,0},send},1}, %% "
- {{{0,257,1},recv},1}, %% CER
+ {{{0,257,1},recv},1}, %% CER
{{{0,257,1},send},3}, %% "
{{{relay,0},recv,{'Result-Code',2001}},2}, %% STA x 2 (send[34])
{{{relay,0},recv,{'Result-Code',3005}},1}, %% ASA (send_loop)
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 4c82d4dee2..434aef01dd 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
%%
%% Tests of traffic between two Diameter nodes, one client, one server.
+%% The traffic isn't meant to be sensible, just to exercise code.
%%
-module(diameter_traffic_SUITE).
@@ -27,13 +28,18 @@
-export([suite/0,
all/0,
groups/0,
+ init_per_suite/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_group/1,
init_per_group/2,
end_per_group/2,
init_per_testcase/2,
end_per_testcase/2]).
%% testcases
--export([start/1,
+-export([rfc4005/1,
+ start/1,
start_services/1,
add_transports/1,
result_codes/1,
@@ -44,6 +50,7 @@
send_protocol_error/1,
send_experimental_result/1,
send_arbitrary/1,
+ send_proxy_info/1,
send_unknown/1,
send_unknown_short/1,
send_unknown_mandatory/1,
@@ -61,6 +68,7 @@
send_invalid_reject/1,
send_unexpected_mandatory_decode/1,
send_unexpected_mandatory/1,
+ send_too_many/1,
send_long/1,
send_maxlen/1,
send_nopeer/1,
@@ -90,21 +98,25 @@
send_multiple_filters_2/1,
send_multiple_filters_3/1,
send_anything/1,
- outstanding/1,
remove_transports/1,
empty/1,
stop_services/1,
stop/1]).
%% diameter callbacks
--export([peer_up/3,
- peer_down/3,
- pick_peer/6, pick_peer/7,
- prepare_request/5, prepare_request/6,
- prepare_retransmit/5,
- handle_answer/6, handle_answer/7,
- handle_error/6,
- handle_request/3]).
+-export([peer_up/4,
+ peer_down/4,
+ pick_peer/7, pick_peer/8,
+ prepare_request/6, prepare_request/7,
+ prepare_retransmit/6,
+ handle_answer/7, handle_answer/8,
+ handle_error/7,
+ handle_request/4]).
+
+%% diameter_{tcp,sctp} callbacks
+-export([message/3]).
+
+-include_lib("kernel/include/inet_sctp.hrl").
-include("diameter.hrl").
-include("diameter_gen_base_rfc3588.hrl").
@@ -115,13 +127,22 @@
%% ===========================================================================
+%% Fraction of shuffle/parallel groups to randomly skip.
+-define(SKIP, 0.25).
+
+%% Positive number of testcases from which to select (randomly) from
+%% tc(), the list of testcases to run, or [] to run all. The random
+%% selection is to limit the time it takes for the suite to run.
+-define(LIMIT, #{tcp => 42, sctp => 5}).
+
-define(util, diameter_util).
-define(A, list_to_atom).
-define(L, atom_to_list).
+-define(B, iolist_to_binary).
%% Don't use is_record/2 since dictionary hrl's aren't included.
-%% (Since they define conflicting reqcords with the same names.)
+%% (Since they define conflicting records with the same names.)
-define(is_record(Rec, Name), (Name == element(1, Rec))).
-define(ADDR, {127,0,0,1}).
@@ -134,32 +155,39 @@
%% Sequence mask for End-to-End and Hop-by-Hop identifiers.
-define(CLIENT_MASK, {1,26}). %% 1 in top 6 bits
-%% How to construct messages, as record or list.
--define(ENCODINGS, [list, record]).
+%% How to construct outgoing messages.
+-define(ENCODINGS, [list, record, map]).
-%% How to send answers, in a diameter_packet or not.
--define(CONTAINERS, [pkt, msg]).
+%% How to decode incoming messages.
+-define(DECODINGS, [record, none, map, list, record_from_map]).
-%% Which common dictionary to use in the clients.
--define(RFCS, [rfc3588, rfc6733]).
+%% Which dictionary to use in the clients.
+-define(RFCS, [rfc3588, rfc6733, rfc4005]).
%% Whether to decode stringish Diameter types to strings, or leave
%% them as binary.
--define(STRING_DECODES, [true, false]).
+-define(STRING_DECODES, [false, true]).
%% Which transport protocol to use.
-define(TRANSPORTS, [tcp, sctp]).
+%% Send from a dedicated process?
+-define(SENDERS, [true, false]).
+
+%% Message callbacks from diameter_{tcp,sctp}?
+-define(CALLBACKS, [true, false]).
+
-record(group,
{transport,
+ strings,
+ encoding,
client_service,
- client_encoding,
- client_dict0,
- client_strings,
+ client_dict,
+ client_sender,
server_service,
- server_encoding,
- server_container,
- server_strings}).
+ server_decoding,
+ server_sender,
+ server_throttle}).
%% Not really what we should be setting unless the message is sent in
%% the common application but diameter doesn't care.
@@ -170,35 +198,37 @@
%% A common match when receiving answers in a client.
-define(answer_message(SessionId, ResultCode),
- ['answer-message',
- {'Session-Id', SessionId},
- {'Origin-Host', _},
- {'Origin-Realm', _},
- {'Result-Code', ResultCode}
- | _]).
+ ['answer-message' | #{'Session-Id' := SessionId,
+ 'Origin-Host' := _,
+ 'Origin-Realm' := _,
+ 'Result-Code' := ResultCode}]).
-define(answer_message(ResultCode),
- ?answer_message(_, ResultCode)).
+ ['answer-message' | #{'Origin-Host' := _,
+ 'Origin-Realm' := _,
+ 'Result-Code' := ResultCode}]).
%% Config for diameter:start_service/2.
--define(SERVICE(Name, Decode),
+-define(SERVICE(Name, Grp),
[{'Origin-Host', Name ++ "." ++ ?REALM},
{'Origin-Realm', ?REALM},
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
{'Product-Name', "OTP/diameter"},
- {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
- {'Acct-Application-Id', [?DIAMETER_APP_ID_ACCOUNTING]},
+ {'Auth-Application-Id', [0]}, %% common messages
+ {'Acct-Application-Id', [3]}, %% base accounting
{restrict_connections, false},
- {string_decode, Decode},
- {incoming_maxlen, 1 bsl 21},
- {spawn_opt, [{min_heap_size, 5000}]}
+ {string_decode, Grp#group.strings},
+ {avp_dictionaries, [diameter_gen_doic_rfc7683]},
+ {incoming_maxlen, 1 bsl 21}
| [{application, [{dictionary, D},
- {module, ?MODULE},
+ {module, [?MODULE, Grp]},
{answer_errors, callback}]}
|| D <- [diameter_gen_base_rfc3588,
diameter_gen_base_accounting,
diameter_gen_base_rfc6733,
- diameter_gen_acct_rfc6733]]]).
+ diameter_gen_acct_rfc6733,
+ nas4005],
+ D /= nas4005 orelse have_nas()]]).
-define(SUCCESS,
?'DIAMETER_BASE_RESULT-CODE_SUCCESS').
@@ -216,6 +246,8 @@
?'DIAMETER_BASE_RESULT-CODE_AVP_UNSUPPORTED').
-define(UNSUPPORTED_VERSION,
?'DIAMETER_BASE_RESULT-CODE_UNSUPPORTED_VERSION').
+-define(TOO_MANY,
+ ?'DIAMETER_BASE_RESULT-CODE_AVP_OCCURS_TOO_MANY_TIMES').
-define(REALM_NOT_SERVED,
?'DIAMETER_BASE_RESULT-CODE_REALM_NOT_SERVED').
-define(UNABLE_TO_DELIVER,
@@ -243,76 +275,169 @@ suite() ->
[{timetrap, {seconds, 10}}].
all() ->
- [start, result_codes, {group, traffic}, outstanding, empty, stop].
+ [rfc4005, start, result_codes, {group, traffic}, empty, stop].
+
+%% Redefine this to run one or more groups for debugging purposes.
+-define(GROUPS, []).
+%-define(GROUPS, [[tcp,rfc6733,record,map,false,false,false,false]]).
+%% Issues with gen_sctp sporadically cause huge numbers of failed
+%% testcases when running testcases in parallel.
groups() ->
- Ts = tc(),
- Sctp = ?util:have_sctp(),
- [{B, [P], Ts} || {B,P} <- [{true, shuffle}, {false, parallel}]]
+ Names = names(),
+ [{P, [P], Ts} || Ts <- [tc()], P <- [shuffle, parallel]]
++
- [{?util:name([T,R,D,A,C,SD,CD]),
- [],
- [start_services,
- add_transports,
- result_codes,
- {group, SD orelse CD},
- remove_transports,
- stop_services]}
- || T <- ?TRANSPORTS,
- T /= sctp orelse Sctp,
- R <- ?ENCODINGS,
- D <- ?RFCS,
- A <- ?ENCODINGS,
- C <- ?CONTAINERS,
- SD <- ?STRING_DECODES,
- CD <- ?STRING_DECODES]
+ [{?util:name(N), [], [{group, if T == sctp; S -> shuffle;
+ true -> parallel end}]}
+ || [T,_,_,_,S|_] = N <- Names]
++
- [{traffic, [], [{group, ?util:name([T,R,D,A,C,SD,CD])}
- || T <- ?TRANSPORTS,
- T /= sctp orelse Sctp,
- R <- ?ENCODINGS,
- D <- ?RFCS,
- A <- ?ENCODINGS,
- C <- ?CONTAINERS,
- SD <- ?STRING_DECODES,
- CD <- ?STRING_DECODES]}].
+ [{T, [], [{group, ?util:name(N)} || N <- names(Names, ?GROUPS),
+ T == hd(N)]}
+ || T <- ?TRANSPORTS]
+ ++
+ [{traffic, [], [{group, T} || T <- ?TRANSPORTS]}].
+
+names() ->
+ [[T,R,E,D,S,ST,SS,CS] || T <- ?TRANSPORTS,
+ R <- ?RFCS,
+ E <- ?ENCODINGS,
+ D <- ?DECODINGS,
+ S <- ?STRING_DECODES,
+ ST <- ?CALLBACKS,
+ SS <- ?SENDERS,
+ CS <- ?SENDERS].
+
+names(Names, []) ->
+ [N || N <- Names,
+ [CS,SS|_] <- [lists:reverse(N)],
+ SS orelse CS]; %% avoid deadlock
+
+names(_, Names) ->
+ Names.
+
+%% --------------------
+
+init_per_suite() ->
+ [{timetrap, {seconds, 60}}].
+
+init_per_suite(Config) ->
+ [{rfc4005, compile_and_load()}, {sctp, ?util:have_sctp()} | Config].
+
+end_per_suite(_Config) ->
+ code:delete(nas4005),
+ code:purge(nas4005),
+ ok.
+
+%% --------------------
+
+init_per_group(_) ->
+ [{timetrap, {seconds, 30}}].
+
+init_per_group(Name, Config)
+ when Name == shuffle;
+ Name == parallel ->
+ case rand:uniform() < ?SKIP of
+ true ->
+ {skip, random};
+ false ->
+ start_services(Config),
+ add_transports(Config),
+ replace({sleep, Name == parallel}, Config)
+ end;
+
+init_per_group(sctp = Name, Config) ->
+ {_, Sctp} = lists:keyfind(Name, 1, Config),
+ if Sctp ->
+ Config;
+ true ->
+ {skip, Name}
+ end;
init_per_group(Name, Config) ->
+ Nas = proplists:get_value(rfc4005, Config, false),
case ?util:name(Name) of
- [T,R,D,A,C,SD,CD] ->
+ [_,R,_,_,_,_,_,_] when R == rfc4005, true /= Nas ->
+ {skip, rfc4005};
+ [T,R,E,D,S,ST,SS,CS] ->
G = #group{transport = T,
+ strings = S,
+ encoding = E,
client_service = [$C|?util:unique_string()],
- client_encoding = R,
- client_dict0 = dict0(D),
- client_strings = CD,
+ client_dict = appdict(R),
+ client_sender = CS,
server_service = [$S|?util:unique_string()],
- server_encoding = A,
- server_container = C,
- server_strings = SD},
- [{group, G} | Config];
+ server_decoding = D,
+ server_sender = SS,
+ server_throttle = ST},
+ replace([{group, G}, {runlist, select(T)}], Config);
_ ->
Config
end.
+end_per_group(Name, Config)
+ when Name == shuffle;
+ Name == parallel ->
+ remove_transports(Config),
+ stop_services(Config);
+
end_per_group(_, _) ->
ok.
+select(T) ->
+ try maps:get(T, ?LIMIT) of
+ N ->
+ lists:sublist(?util:scramble(tc()), max(5, rand:uniform(N)))
+ catch
+ error:_ -> ?LIMIT
+ end.
+
+%% --------------------
+
+%% Work around common_test accumulating Config improperly, causing
+%% testcases to get Config from groups and suites they're not in.
+init_per_testcase(N, Config)
+ when N == rfc4005;
+ N == start;
+ N == result_codes;
+ N == empty;
+ N == stop ->
+ Config;
+
%% Skip testcases that can reasonably fail under SCTP.
init_per_testcase(Name, Config) ->
- case [skip || #group{transport = sctp}
- <- [proplists:get_value(group, Config)],
- send_maxlen == Name
- orelse send_long == Name]
+ TCs = proplists:get_value(runlist, Config, []),
+ Run = [] == TCs orelse lists:member(Name, TCs),
+ case [G || #group{transport = sctp} = G
+ <- [proplists:get_value(group, Config)]]
of
- [skip] ->
+ [_] when Name == send_maxlen;
+ Name == send_long ->
{skip, sctp};
- [] ->
+ _ when not Run ->
+ {skip, random};
+ _ ->
+ proplists:get_value(sleep, Config, false)
+ andalso timer:sleep(rand:uniform(200)),
[{testcase, Name} | Config]
end.
end_per_testcase(_, _) ->
ok.
+%% replace/2
+%%
+%% Work around common_test running init functions inappropriately, and
+%% this accumulating more config than expected.
+
+replace(Pairs, Config)
+ when is_list(Pairs) ->
+ lists:foldl(fun replace/2, Config, Pairs);
+
+replace({Key, _} = T, Config) ->
+ [T | lists:keydelete(Key, 1, Config)].
+
+%% --------------------
+
%% Testcases to run when services are started and connections
%% established.
tc() ->
@@ -323,6 +448,7 @@ tc() ->
send_protocol_error,
send_experimental_result,
send_arbitrary,
+ send_proxy_info,
send_unknown,
send_unknown_short,
send_unknown_mandatory,
@@ -340,6 +466,7 @@ tc() ->
send_invalid_reject,
send_unexpected_mandatory_decode,
send_unexpected_mandatory,
+ send_too_many,
send_long,
send_maxlen,
send_nopeer,
@@ -378,47 +505,87 @@ start(_Config) ->
start_services(Config) ->
#group{client_service = CN,
- client_strings = CD,
server_service = SN,
- server_strings = SD}
+ server_decoding = SD}
+ = Grp
= group(Config),
- ok = diameter:start_service(SN, ?SERVICE(SN, SD)),
- ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK}
- | ?SERVICE(CN, CD)]).
+ ok = diameter:start_service(SN, [{traffic_counters, bool()},
+ {decode_format, SD}
+ | ?SERVICE(SN, Grp)]),
+ ok = diameter:start_service(CN, [{traffic_counters, bool()},
+ {sequence, ?CLIENT_MASK},
+ {decode_format, map},
+ {strict_arities, decode}
+ | ?SERVICE(CN, Grp)]).
+
+bool() ->
+ 0.5 =< rand:uniform().
add_transports(Config) ->
#group{transport = T,
+ encoding = E,
client_service = CN,
- server_service = SN}
- = group(Config),
+ client_sender = CS,
+ server_service = SN,
+ server_sender = SS,
+ server_throttle = ST}
+ = group(Config),
LRef = ?util:listen(SN,
- T,
+ [T,
+ {sender, SS},
+ {message_cb, ST andalso {?MODULE, message, [0]}}]
+ ++ [{packet, hd(?util:scramble([false, raw]))}
+ || T == sctp andalso CS]
+ ++ [{unordered, unordered()} || T == sctp],
[{capabilities_cb, fun capx/2},
- {pool_size, 8},
- {spawn_opt, [{min_heap_size, 8096}]},
- {applications, apps(rfc3588)}]),
+ {pool_size, 8}
+ | server_apps()]
+ ++ [{spawn_opt, {erlang, spawn, []}} || CS]),
Cs = [?util:connect(CN,
- T,
+ [T, {sender, CS} | client_opts(T)],
LRef,
- [{id, Id},
- {capabilities, [{'Origin-State-Id', origin(Id)}]},
- {applications, apps(D)}])
- || A <- ?ENCODINGS,
- C <- ?CONTAINERS,
- D <- ?RFCS,
- Id <- [{A,C}]],
- %% The server uses the client's Origin-State-Id to decide how to
- %% answer.
+ [{id, Id}
+ | client_apps(R, [{'Origin-State-Id', origin(Id)}])])
+ || D <- ?DECODINGS, %% for multiple candidate peers
+ R <- ?RFCS,
+ R /= rfc4005 orelse have_nas(),
+ Id <- [{D,E}]],
?util:write_priv(Config, "transport", [LRef | Cs]).
-apps(D0) ->
- D = dict0(D0),
- [acct(D), D].
+unordered() ->
+ element(rand:uniform(4), {true, false, 1, 2}).
+
+client_opts(tcp) ->
+ [];
+client_opts(sctp) ->
+ [{unordered, unordered()}
+ | [{sctp_initmsg, #sctp_initmsg{num_ostreams = N,
+ max_instreams = 5}}
+ || N <- [rand:uniform(8)],
+ N =< 6]].
+
+server_apps() ->
+ B = have_nas(),
+ [{applications, [diameter_gen_base_rfc3588,
+ diameter_gen_base_accounting]
+ ++ [nas4005 || B]},
+ {capabilities, [{'Auth-Application-Id', [0] ++ [1 || B]}, %% common, NAS
+ {'Acct-Application-Id', [3]}]}]. %% accounting
+
+client_apps(D, Caps) ->
+ if D == rfc4005 ->
+ [{applications, [nas4005]},
+ {capabilities, [{'Auth-Application-Id', [1]}, %% NAS
+ {'Acct-Application-Id', []}
+ | Caps]}];
+ true ->
+ D0 = dict0(D),
+ [{applications, [acct(D0), D0]},
+ {capabilities, Caps}]
+ end.
-%% Ensure there are no outstanding requests in request table.
-outstanding(_Config) ->
- [] = [T || T <- ets:tab2list(diameter_request),
- is_atom(element(1,T))].
+have_nas() ->
+ false /= code:is_loaded(nas4005).
remove_transports(Config) ->
#group{client_service = CN,
@@ -451,9 +618,16 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) ->
%% ===========================================================================
+%% Fail only this testcase if the RFC 4005 dictionary hasn't been
+%% successfully compiled and loaded.
+rfc4005(Config) ->
+ true = proplists:get_value(rfc4005, Config).
+
%% Ensure that result codes have the expected values.
result_codes(_Config) ->
- {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011, 5014}
+ {2001,
+ 3001, 3002, 3003, 3004, 3007, 3008, 3009,
+ 5001, 5009, 5011, 5014}
= {?SUCCESS,
?COMMAND_UNSUPPORTED,
?UNABLE_TO_DELIVER,
@@ -463,6 +637,7 @@ result_codes(_Config) ->
?INVALID_HDR_BITS,
?INVALID_AVP_BITS,
?AVP_UNSUPPORTED,
+ ?TOO_MANY,
?UNSUPPORTED_VERSION,
?INVALID_AVP_LENGTH}.
@@ -470,8 +645,8 @@ result_codes(_Config) ->
send_ok(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 1}],
-
- ['ACA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['ACA' | #{'Result-Code' := ?SUCCESS,
+ 'Session-Id' := _}]
= call(Config, Req).
%% Send an accounting ACR that the server answers badly to.
@@ -487,7 +662,8 @@ send_eval(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 3}],
- ['ACA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['ACA' | #{'Result-Code' := ?SUCCESS,
+ 'Session-Id' := _}]
= call(Config, Req).
%% Send an accounting ACR that the server tries to answer with an
@@ -500,20 +676,87 @@ send_bad_answer(Config) ->
= call(Config, Req).
%% Send an ACR that the server callback answers explicitly with a
-%% protocol error.
+%% protocol error and some AVPs to check the decoding of.
send_protocol_error(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 4}],
- ?answer_message(?TOO_BUSY)
- = call(Config, Req).
+ ['answer-message' | #{'Result-Code' := ?TOO_BUSY,
+ 'AVP' := [OLR | _]} = Avps]
+ = call(Config, Req),
+
+ #diameter_avp{name = 'OC-OLR',
+ value = #{'OC-Sequence-Number' := 1,
+ 'OC-Report-Type' := 0, %% HOST_REPORT
+ 'OC-Reduction-Percentage' := [25],
+ 'OC-Validity-Duration' := [60],
+ 'AVP' := [OSF]}}
+ = OLR,
+ #diameter_avp{name = 'OC-Supported-Features',
+ value = #{} = Fs}
+ = OSF,
+ 0 = maps:size(Fs),
+
+ #group{client_dict = D} = group(Config),
+
+ if D == nas4005 ->
+ error = maps:find('Failed-AVP', Avps),
+ #{'AVP' := [_,Failed]}
+ = Avps,
+ #diameter_avp{name = 'Failed-AVP',
+ value = #{'AVP' := [NP,FR,AP]}}
+ = Failed,
+ #diameter_avp{name = 'NAS-Port',
+ value = 44}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP;
+
+ D == diameter_gen_base_rfc3588;
+ D == diameter_gen_basr_accounting ->
+ error = maps:find('Failed-AVP', Avps),
+ #{'AVP' := [_,Failed]}
+ = Avps,
+
+ #diameter_avp{name = 'Failed-AVP',
+ value = #{'AVP' := [NP,FR,AP]}}
+ = Failed,
+ #diameter_avp{name = undefined,
+ value = undefined}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP;
+
+ D == diameter_gen_base_rfc6733;
+ D == diameter_gen_acct_rfc6733 ->
+ #{'Failed-AVP' := [#{'AVP' := [NP,FR,AP]}],
+ 'AVP' := [_]}
+ = Avps,
+ #diameter_avp{name = undefined,
+ value = undefined}
+ = NP,
+ #diameter_avp{name = 'Firmware-Revision',
+ value = 12}
+ = FR,
+ #diameter_avp{name = 'Auth-Grace-Period',
+ value = 13}
+ = AP
+ end.
%% Send a 3xxx Experimental-Result in an answer not setting the E-bit
%% and missing a Result-Code.
send_experimental_result(Config) ->
Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD},
{'Accounting-Record-Number', 5}],
- ['ACA', {'Session-Id', _} | _]
+ ['ACA' | #{'Session-Id' := _}]
= call(Config, Req).
%% Send an ASR with an arbitrary non-mandatory AVP and expect success
@@ -521,24 +764,37 @@ send_experimental_result(Config) ->
send_arbitrary(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{name = 'Product-Name',
value = "XXX"}]}],
- ['ASA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | Avps]
+ ['ASA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS,
+ 'AVP' := [#diameter_avp{name = 'Product-Name',
+ value = V}]}]
= call(Config, Req),
- {'AVP', [#diameter_avp{name = 'Product-Name',
- value = V}]}
- = lists:last(Avps),
"XXX" = string(V, Config).
+%% Send Proxy-Info in an ASR that the peer answers with 3xxx, and
+%% ensure that the AVP is returned.
+send_proxy_info(Config) ->
+ H0 = ?B(?util:unique_string()),
+ S0 = ?B(?util:unique_string()),
+ Req = ['ASR', {'Proxy-Info', #{'Proxy-Host' => H0,
+ 'Proxy-State' => S0}}],
+ ['answer-message' | #{'Result-Code' := 3999,
+ 'Proxy-Info' := [#{'Proxy-Host' := H,
+ 'Proxy-State' := S}]}]
+ = call(Config, Req),
+ [H0, S0] = [?B(X) || X <- [H,S]].
+
%% Send an unknown AVP (to some client) and check that it comes back.
send_unknown(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
is_mandatory = false,
data = <<17>>}]}],
- ['ASA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | Avps]
- = call(Config, Req),
- {'AVP', [#diameter_avp{code = 999,
- is_mandatory = false,
- data = <<17>>}]}
- = lists:last(Avps).
+ ['ASA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS,
+ 'AVP' := [#diameter_avp{code = 999,
+ is_mandatory = false,
+ data = <<17>>}]}]
+ = call(Config, Req).
%% Ditto, and point the AVP length past the end of the message. Expect
%% 5014.
@@ -549,28 +805,28 @@ send_unknown_short(Config, M, RC) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
is_mandatory = M,
data = <<17>>}]}],
- ['ASA', {'Session-Id', _}, {'Result-Code', RC} | Avps]
+ ['ASA' | #{'Session-Id' := _,
+ 'Result-Code' := RC,
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [#'diameter_base_Failed-AVP'{'AVP' = As}]
- = proplists:get_value('Failed-AVP', Avps),
- [#diameter_avp{code = 999,
- is_mandatory = M,
- data = <<17, _/binary>>}] %% extra bits from padding
- = As.
+ #diameter_avp{code = 999,
+ is_mandatory = M,
+ data = <<17, _/binary>>} %% extra bits from padding
+ = Avp.
%% Ditto but set the M flag.
send_unknown_mandatory(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 999,
is_mandatory = true,
data = <<17>>}]}],
- ['ASA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | Avps]
+ ['ASA' | #{'Session-Id' := _,
+ 'Result-Code' := ?AVP_UNSUPPORTED,
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [#'diameter_base_Failed-AVP'{'AVP' = As}]
- = proplists:get_value('Failed-AVP', Avps),
- [#diameter_avp{code = 999,
- is_mandatory = true,
- data = <<17>>}]
- = As.
+ #diameter_avp{code = 999,
+ is_mandatory = true,
+ data = <<17>>}
+ = Avp.
%% Ditto, and point the AVP length past the end of the message. Expect
%% 5014 instead of 5001.
@@ -583,15 +839,27 @@ send_unexpected_mandatory_decode(Config) ->
Req = ['ASR', {'AVP', [#diameter_avp{code = 27, %% Session-Timeout
is_mandatory = true,
data = <<12:32>>}]}],
- ['ASA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | Avps]
+ ['ASA' | #{'Session-Id' := _,
+ 'Result-Code' := ?AVP_UNSUPPORTED,
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [#'diameter_base_Failed-AVP'{'AVP' = As}]
- = proplists:get_value('Failed-AVP', Avps),
- [#diameter_avp{code = 27,
- is_mandatory = true,
- value = 12,
- data = <<12:32>>}]
- = As.
+ #diameter_avp{code = 27,
+ is_mandatory = true,
+ value = 12,
+ data = <<12:32>>}
+ = Avp.
+
+%% Try to two Auth-Application-Id in ASR expect 5009.
+send_too_many(Config) ->
+ Req = ['ASR', {'Auth-Application-Id', [?APP_ID, 44]}],
+
+ ['ASA' | #{'Session-Id' := _,
+ 'Result-Code' := ?TOO_MANY,
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
+ = call(Config, Req),
+ #diameter_avp{name = 'Auth-Application-Id',
+ value = 44}
+ = Avp.
%% Send an containing a faulty Grouped AVP (empty Proxy-Host in
%% Proxy-Info) and expect that only the faulty AVP is sent in
@@ -601,16 +869,13 @@ send_unexpected_mandatory_decode(Config) ->
send_grouped_error(Config) ->
Req = ['ASR', {'Proxy-Info', [[{'Proxy-Host', "abcd"},
{'Proxy-State', ""}]]}],
- ['ASA', {'Session-Id', _}, {'Result-Code', ?INVALID_AVP_LENGTH} | Avps]
+ ['ASA' | #{'Session-Id' := _,
+ 'Result-Code' := ?INVALID_AVP_LENGTH,
+ 'Failed-AVP' := [#{'AVP' := [Avp]}]}]
= call(Config, Req),
- [#'diameter_base_Failed-AVP'{'AVP' = As}]
- = proplists:get_value('Failed-AVP', Avps),
- [#diameter_avp{name = 'Proxy-Info',
- value = #'diameter_base_Proxy-Info'
- {'Proxy-Host' = Empty,
- 'Proxy-State' = undefined}}]
- = As,
- <<0>> = iolist_to_binary(Empty).
+ #diameter_avp{name = 'Proxy-Info', value = #{'Proxy-Host' := H}}
+ = Avp,
+ <<0>> = ?B(H).
%% Send an STR that the server ignores.
send_noreply(Config) ->
@@ -638,7 +903,8 @@ send_error_bit(Config) ->
%% Send a bad version and check that we get 5011.
send_unsupported_version(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- ['STA', {'Session-Id', _}, {'Result-Code', ?UNSUPPORTED_VERSION} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?UNSUPPORTED_VERSION}]
= call(Config, Req).
%% Send a request containing an AVP length > data size.
@@ -658,16 +924,11 @@ send_zero_avp_length(Config) ->
send_invalid_avp_length(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- ['STA', {'Session-Id', _},
- {'Result-Code', ?INVALID_AVP_LENGTH},
- {'Origin-Host', _},
- {'Origin-Realm', _},
- {'User-Name', _},
- {'Class', _},
- {'Error-Message', _},
- {'Error-Reporting-Host', _},
- {'Failed-AVP', [#'diameter_base_Failed-AVP'{'AVP' = [_]}]}
- | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?INVALID_AVP_LENGTH,
+ 'Origin-Host' := _,
+ 'Origin-Realm' := _,
+ 'Failed-AVP' := [#{'AVP' := [_]}]}]
= call(Config, Req).
%% Send a request containing 5xxx errors that the server rejects with
@@ -683,20 +944,22 @@ send_invalid_reject(Config) ->
send_unexpected_mandatory(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- ['STA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?AVP_UNSUPPORTED}]
= call(Config, Req).
%% Send something long that will be fragmented by TCP.
send_long(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
- {'User-Name', [lists:duplicate(1 bsl 20, $X)]}],
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ {'User-Name', [binary:copy(<<$X>>, 1 bsl 20)]}],
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
= call(Config, Req).
%% Send something longer than the configure incoming_maxlen.
send_maxlen(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
- {'User-Name', [lists:duplicate(1 bsl 21, $X)]}],
+ {'User-Name', [binary:copy(<<$X>>, 1 bsl 21)]}],
{timeout, _} = call(Config, Req).
%% Send something for which pick_peer finds no suitable peer.
@@ -733,7 +996,8 @@ send_any_2(Config) ->
send_all_1(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
Realm = lists:foldr(fun(C,A) -> [C,A] end, [], ?REALM),
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
= call(Config, Req, [{filter, {all, [{host, any},
{realm, Realm}]}}]).
send_all_2(Config) ->
@@ -762,13 +1026,13 @@ send_detach(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
Ref = make_ref(),
ok = call(Config, Req, [{extra, [{self(), Ref}]}, detach]),
- Ans = receive {Ref, T} -> T end,
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
- = Ans.
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
+ = receive {Ref, T} -> T end.
%% Send a request which can't be encoded and expect {error, encode}.
send_encode_error(Config) ->
- {error, encode} = call(Config, ['STR']). %% No Termination-Cause
+ {error, encode} = call(Config, ['STR', {'Termination-Cause', huh}]).
%% Send with filtering and expect success.
send_destination_1(Config) ->
@@ -776,25 +1040,27 @@ send_destination_1(Config) ->
= group(Config),
Req = ['STR', {'Termination-Cause', ?LOGOUT},
{'Destination-Host', [?HOST(SN, ?REALM)]}],
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
= call(Config, Req, [{filter, {all, [host, realm]}}]).
send_destination_2(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
= call(Config, Req, [{filter, {all, [host, realm]}}]).
%% Send with filtering on and expect failure when specifying an
%% unknown host or realm.
send_destination_3(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
- {'Destination-Realm', "unknown.org"}],
+ {'Destination-Realm', <<"unknown.org">>}],
{error, no_connection}
= call(Config, Req, [{filter, {all, [host, realm]}}]).
send_destination_4(Config) ->
#group{server_service = SN}
= group(Config),
Req = ['STR', {'Termination-Cause', ?LOGOUT},
- {'Destination-Host', [?HOST(SN, "unknown.org")]}],
+ {'Destination-Host', [?HOST(SN, ["unknown.org"])]}],
{error, no_connection}
= call(Config, Req, [{filter, {all, [host, realm]}}]).
@@ -802,7 +1068,7 @@ send_destination_4(Config) ->
%% an unknown host or realm.
send_destination_5(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT},
- {'Destination-Realm', "unknown.org"}],
+ {'Destination-Realm', [<<"unknown.org">>]}],
?answer_message(?REALM_NOT_SERVED)
= call(Config, Req).
send_destination_6(Config) ->
@@ -844,7 +1110,8 @@ send_bad_filter(Config, F) ->
%% Specify multiple filter options and expect them be conjunctive.
send_multiple_filters_1(Config) ->
Fun = fun(#diameter_caps{}) -> true end,
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
= send_multiple_filters(Config, [host, {eval, Fun}]).
send_multiple_filters_2(Config) ->
E = {erlang, is_tuple, []},
@@ -855,7 +1122,8 @@ send_multiple_filters_3(Config) ->
E2 = {erlang, is_tuple, []},
E3 = {erlang, is_record, [diameter_caps]},
E4 = [{erlang, is_record, []}, diameter_caps],
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
= send_multiple_filters(Config, [{eval, E} || E <- [E1,E2,E3,E4]]).
send_multiple_filters(Config, Fs) ->
@@ -866,7 +1134,8 @@ send_multiple_filters(Config, Fs) ->
%% only the return value from the prepare_request callback being
%% significant.
send_anything(Config) ->
- ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _]
+ ['STA' | #{'Session-Id' := _,
+ 'Result-Code' := ?SUCCESS}]
= call(Config, anything).
%% ===========================================================================
@@ -875,7 +1144,7 @@ group(Config) ->
#group{} = proplists:get_value(group, Config).
string(V, Config) ->
- #group{client_strings = B} = group(Config),
+ #group{strings = B} = group(Config),
decode(V,B).
decode(S, true)
@@ -890,58 +1159,137 @@ call(Config, Req) ->
call(Config, Req, Opts) ->
Name = proplists:get_value(testcase, Config),
- #group{client_service = CN,
- client_encoding = ReqEncoding,
- client_dict0 = Dict0}
- = Group
+ #group{encoding = Enc,
+ client_service = CN,
+ client_dict = Dict0}
= group(Config),
diameter:call(CN,
dict(Req, Dict0),
- msg(Req, ReqEncoding, Dict0),
- [{extra, [{Name, Group}, diameter_lib:now()]} | Opts]).
+ msg(Req, Enc, Dict0),
+ [{extra, [Name, diameter_lib:now()]} | Opts]).
-origin({A,C}) ->
- 2*codec(A) + container(C);
+origin({D,E}) ->
+ 4*decode(D) + encode(E);
origin(N) ->
- {codec(N band 2), container(N rem 2)}.
-
-%% Map booleans, but the readable atoms are part of (constructed)
-%% group names, so it's good that they're readable.
-
-codec(record) -> 0;
-codec(list) -> 1;
-codec(0) -> record;
-codec(_) -> list.
-
-container(pkt) -> 0;
-container(msg) -> 1;
-container(0) -> pkt;
-container(_) -> msg.
+ {decode(N bsr 2), encode(N rem 4)}.
+
+%% Map atoms. The atoms are part of (constructed) group names, so it's
+%% good that they're readable.
+
+decode(record) -> 0;
+decode(list) -> 1;
+decode(map) -> 2;
+decode(none) -> 3;
+decode(record_from_map) -> 4;
+decode(0) -> record;
+decode(1) -> list;
+decode(2) -> map;
+decode(3) -> none;
+decode(4) -> record_from_map.
+
+encode(record) -> 0;
+encode(list) -> 1;
+encode(map) -> 2;
+encode(0) -> record;
+encode(1) -> list;
+encode(2) -> map.
msg([H|_] = Msg, record = E, diameter_gen_base_rfc3588)
when H == 'ACR';
H == 'ACA' ->
msg(Msg, E, diameter_gen_base_accounting);
+
msg([H|_] = Msg, record = E, diameter_gen_base_rfc6733)
when H == 'ACR';
H == 'ACA' ->
msg(Msg, E, diameter_gen_acct_rfc6733);
+
msg([H|T], record, Dict) ->
Dict:'#new-'(Dict:msg2rec(H), T);
+
+msg([H|As], map, _)
+ when is_list(As) ->
+ [H | maps:from_list(As)];
+
msg(Msg, _, _) ->
Msg.
+to_map(#diameter_packet{msg = [_MsgName | Avps] = Msg},
+ #group{server_decoding = map})
+ when is_map(Avps) ->
+ Msg;
+
+to_map(#diameter_packet{msg = [MsgName | Avps]},
+ #group{server_decoding = list}) ->
+ [MsgName | maps:from_list(Avps)];
+
+to_map(#diameter_packet{header = H, msg = Rec},
+ #group{server_decoding = D})
+ when D == record;
+ D == record_from_map ->
+ rec_to_map(Rec, dict(H));
+
+%% No record decode: do it ourselves.
+to_map(#diameter_packet{header = H,
+ msg = Name,
+ bin = Bin},
+ #group{server_decoding = none,
+ strings = B}) ->
+ Opts = #{decode_format => map,
+ string_decode => B,
+ avp_dictionaries => [diameter_gen_doic_rfc7683],
+ strict_mbit => true,
+ rfc => 6733},
+ #diameter_packet{msg = [MsgName | _Map] = Msg}
+ = diameter_codec:decode(dict(H), Opts, Bin),
+ {MsgName, _} = {Name, Msg}, %% assert
+ Msg.
+
+dict(#diameter_header{application_id = Id,
+ cmd_code = Code}) ->
+ if Id == 1 ->
+ nas4005;
+ Code == 271 ->
+ diameter_gen_base_accounting;
+ true ->
+ diameter_gen_base_rfc3588
+ end.
+
+rec_to_map(Rec, Dict) ->
+ [R | Vs] = Dict:'#get-'(Rec),
+ [Dict:rec2msg(R) | maps:from_list([T || {_,V} = T <- Vs,
+ V /= undefined,
+ V /= []])].
+
+appdict(rfc4005) ->
+ nas4005;
+appdict(D) ->
+ dict0(D).
+
dict0(D) ->
?A("diameter_gen_base_" ++ ?L(D)).
-dict(Msg, Dict0)
- when 'ACR' == hd(Msg);
- 'ACA' == hd(Msg);
- ?is_record(Msg, diameter_base_accounting_ACR);
- ?is_record(Msg, diameter_base_accounting_ACA) ->
+dict(Msg, Dict) ->
+ d(name(Msg), Dict).
+
+d(N, nas4005 = D) ->
+ if N == {list, 'answer-message'};
+ N == {map, 'answer-message'};
+ N == {record, 'diameter_base_answer-message'} ->
+ diameter_gen_base_rfc3588;
+ true ->
+ D
+ end;
+d(N, Dict0)
+ when N == {list, 'ACR'};
+ N == {list, 'ACA'};
+ N == {map, 'ACR'};
+ N == {map, 'ACA'};
+ N == {record, diameter_base_accounting_ACR};
+ N == {record, diameter_base_accounting_ACA} ->
acct(Dict0);
-dict(_, Dict0) ->
+d(_, Dict0) ->
Dict0.
acct(diameter_gen_base_rfc3588) ->
@@ -950,53 +1298,60 @@ acct(diameter_gen_base_rfc6733) ->
diameter_gen_acct_rfc6733.
%% Set only values that aren't already.
-set(_, [H|T], Vs) ->
- [H | Vs ++ T];
-set(#group{client_dict0 = Dict0} = _Group, Rec, Vs) ->
+
+set(_, [N | As], Vs) ->
+ [N | if is_map(As) ->
+ maps:merge(maps:from_list(Vs), As);
+ is_list(As) ->
+ Vs ++ As
+ end];
+
+set(#group{client_dict = Dict0} = _Group, Rec, Vs) ->
Dict = dict(Rec, Dict0),
lists:foldl(fun({F,_} = FV, A) ->
- set(Dict, Dict:'#get-'(F, A), FV, A)
+ reset(Dict, Dict:'#get-'(F, A), FV, A)
end,
Rec,
Vs).
-set(Dict, E, FV, Rec)
+reset(Dict, E, FV, Rec)
when E == undefined;
E == [] ->
Dict:'#set-'(FV, Rec);
-set(_, _, _, Rec) ->
+
+reset(_, _, _, Rec) ->
Rec.
%% ===========================================================================
%% diameter callbacks
-%% peer_up/3
+%% peer_up/4
-peer_up(_SvcName, _Peer, State) ->
+peer_up(_SvcName, _Peer, State, _Group) ->
State.
%% peer_down/3
-peer_down(_SvcName, _Peer, State) ->
+peer_down(_SvcName, _Peer, State, _Group) ->
State.
-%% pick_peer/6-7
+%% pick_peer/7-8
-pick_peer(Peers, _, [$C|_], _State, {Name, Group}, _)
+pick_peer(Peers, _, [$C|_], _State, Group, Name, _)
when Name /= send_detach ->
find(Group, Peers).
-pick_peer(_Peers, _, [$C|_], _State, {send_nopeer, _}, _, ?EXTRA) ->
+pick_peer(_Peers, _, [$C|_], _State, _Group, send_nopeer, _, ?EXTRA) ->
false;
-pick_peer(Peers, _, [$C|_], _State, {send_detach, Group}, _, {_,_}) ->
+pick_peer(Peers, _, [$C|_], _State, Group, send_detach, _, {_,_}) ->
find(Group, Peers).
-find(#group{client_service = CN,
- server_encoding = A,
- server_container = C},
- Peers) ->
- Id = {A,C},
+find(#group{encoding = E,
+ client_service = CN,
+ server_decoding = D},
+ [_|_] = Peers) ->
+ Id = {D,E},
[P] = [P || P <- Peers, id(Id, P, CN)],
{ok, P}.
@@ -1005,15 +1360,15 @@ id(Id, {Pid, _Caps}, SvcName) ->
= diameter:service_info(SvcName, Pid),
lists:member({id, Id}, Opts).
-%% prepare_request/5-6
+%% prepare_request/6-7
-prepare_request(_Pkt, [$C|_], {_Ref, _Caps}, {send_discard, _}, _) ->
+prepare_request(_Pkt, [$C|_], {_Ref, _Caps}, _, send_discard, _) ->
{discard, unprepared};
-prepare_request(Pkt, [$C|_], {_Ref, Caps}, {Name, Group}, _) ->
+prepare_request(Pkt, [$C|_], {_Ref, Caps}, Group, Name, _) ->
{send, prepare(Pkt, Caps, Name, Group)}.
-prepare_request(Pkt, [$C|_], {_Ref, Caps}, {send_detach, Group}, _, _) ->
+prepare_request(Pkt, [$C|_], {_Ref, Caps}, Group, send_detach, _, _) ->
{eval_packet, {send, prepare(Pkt, Caps, Group)}, [fun log/2, detach]}.
log(#diameter_packet{bin = Bin} = P, T)
@@ -1022,7 +1377,7 @@ log(#diameter_packet{bin = Bin} = P, T)
%% prepare/4
-prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
+prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group)
when N == send_unknown_short_mandatory;
N == send_unknown_short ->
Req = prepare(Pkt, Caps, Group),
@@ -1042,7 +1397,7 @@ prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
<<H:Offset/binary, Len:24, T/binary>> = Bin,
E#diameter_packet{bin = <<H/binary, (Len+9):24, T/binary>>};
-prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
+prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group)
when N == send_long_avp_length;
N == send_short_avp_length;
N == send_zero_avp_length ->
@@ -1068,7 +1423,7 @@ prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
T/binary,
Hdr/binary, AL:24, Data/binary>>};
-prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
+prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group)
when N == send_invalid_avp_length;
N == send_invalid_reject ->
Req = prepare(Pkt, Caps, Group),
@@ -1083,7 +1438,7 @@ prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group)
<<V, L:24, H/binary>> = H0, %% assert
E#diameter_packet{bin = <<V, (L+4):24, H/binary, 16:24, 0:32, T/binary>>};
-prepare(Pkt, Caps, send_unexpected_mandatory, #group{client_dict0 = Dict0}
+prepare(Pkt, Caps, send_unexpected_mandatory, #group{client_dict = Dict0}
= Group) ->
Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<V, Len:24, T/binary>>}
@@ -1093,7 +1448,7 @@ prepare(Pkt, Caps, send_unexpected_mandatory, #group{client_dict0 = Dict0}
Avp = <<Code:32, Flags, 8:24>>,
E#diameter_packet{bin = <<V, (Len+8):24, T/binary, Avp/binary>>};
-prepare(Pkt, Caps, send_grouped_error, #group{client_dict0 = Dict0}
+prepare(Pkt, Caps, send_grouped_error, #group{client_dict = Dict0}
= Group) ->
Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = Bin}
@@ -1125,14 +1480,14 @@ prepare(Pkt, Caps, send_grouped_error, #group{client_dict0 = Dict0}
Payload/binary,
T/binary>>};
-prepare(Pkt, Caps, send_unsupported, #group{client_dict0 = Dict0} = Group) ->
+prepare(Pkt, Caps, send_unsupported, #group{client_dict = Dict0} = Group) ->
Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>}
= E
= diameter_codec:encode(Dict0, Pkt#diameter_packet{msg = Req}),
E#diameter_packet{bin = <<H/binary, 42:24, T/binary>>};
-prepare(Pkt, Caps, send_unsupported_app, #group{client_dict0 = Dict0}
+prepare(Pkt, Caps, send_unsupported_app, #group{client_dict = Dict0}
= Group) ->
Req = prepare(Pkt, Caps, Group),
#diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>}
@@ -1159,93 +1514,120 @@ prepare(Pkt, Caps, _Name, Group) ->
%% prepare/3
-prepare(#diameter_packet{msg = Req}, Caps, Group)
- when ?is_record(Req, diameter_base_accounting_ACR);
- 'ACR' == hd(Req) ->
+prepare(#diameter_packet{msg = Req} = Pkt, Caps, Group) ->
+ set(name(Req), Pkt, Caps, Group).
+
+%% set/4
+
+set(N, #diameter_packet{msg = Req}, Caps, Group)
+ when N == {record, diameter_base_accounting_ACR};
+ N == {record, nas_ACR};
+ N == {map, 'ACR'};
+ N == {list, 'ACR'} ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
- set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Realm', DR}]);
+ set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]},
+ {'Origin-Host', [OH]},
+ {'Origin-Realm', [OR]},
+ {'Destination-Realm', [DR]}]);
-prepare(#diameter_packet{msg = Req}, Caps, Group)
- when ?is_record(Req, diameter_base_ASR);
- 'ASR' == hd(Req) ->
+set(N, #diameter_packet{msg = Req}, Caps, Group)
+ when N == {record, diameter_base_ASR};
+ N == {record, nas_ASR};
+ N == {map, 'ASR'};
+ N == {list, 'ASR'} ->
#diameter_caps{origin_host = {OH, DH},
origin_realm = {OR, DR}}
= Caps,
- set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR},
+ set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]},
+ {'Origin-Host', [OH]},
+ {'Origin-Realm', [OR]},
+ {'Destination-Host', [DH]},
+ {'Destination-Realm', [DR]},
{'Auth-Application-Id', ?APP_ID}]);
-prepare(#diameter_packet{msg = Req}, Caps, Group)
- when ?is_record(Req, diameter_base_STR);
- 'STR' == hd(Req) ->
+set(N, #diameter_packet{msg = Req}, Caps, Group)
+ when N == {record, diameter_base_STR};
+ N == {record, nas_STR};
+ N == {map, 'STR'};
+ N == {list, 'STR'} ->
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
- set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Realm', DR},
+ set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]},
+ {'Origin-Host', [OH]},
+ {'Origin-Realm', [OR]},
+ {'Destination-Realm', [DR]},
{'Auth-Application-Id', ?APP_ID}]);
-prepare(#diameter_packet{msg = Req}, Caps, Group)
- when ?is_record(Req, diameter_base_RAR);
- 'RAR' == hd(Req) ->
+set(N, #diameter_packet{msg = Req}, Caps, Group)
+ when N == {record, diameter_base_RAR};
+ N == {record, nas_RAR};
+ N == {map, 'RAR'};
+ N == {list, 'RAR'} ->
#diameter_caps{origin_host = {OH, DH},
origin_realm = {OR, DR}}
= Caps,
- set(Group, Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
- {'Origin-Realm', OR},
- {'Destination-Host', DH},
- {'Destination-Realm', DR},
+ set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]},
+ {'Origin-Host', [OH]},
+ {'Origin-Realm', [OR]},
+ {'Destination-Host', [DH]},
+ {'Destination-Realm', [DR]},
{'Auth-Application-Id', ?APP_ID}]).
-%% prepare_retransmit/5
+%% name/1
+
+name([H|#{}]) ->
+ {map, H};
+
+name([H|_]) ->
+ {list, H};
+
+name(Rec) ->
+ try
+ {record, element(1, Rec)}
+ catch
+ error: badarg ->
+ false
+ end.
-prepare_retransmit(_Pkt, false, _Peer, _Name, _Group) ->
+%% prepare_retransmit/6
+
+prepare_retransmit(_Pkt, false, _Peer, _Group, _Name, _) ->
discard.
-%% handle_answer/6-7
+%% handle_answer/7-8
-handle_answer(Pkt, Req, [$C|_], Peer, {Name, Group}, _) ->
+handle_answer(Pkt, Req, [$C|_], Peer, Group, Name, _) ->
answer(Pkt, Req, Peer, Name, Group).
-handle_answer(Pkt, Req, [$C|_], Peer, {send_detach = Name, Group}, _, X) ->
+handle_answer(Pkt, Req, [$C|_], Peer, Group, send_detach = Name, _, X) ->
{Pid, Ref} = X,
Pid ! {Ref, answer(Pkt, Req, Peer, Name, Group)}.
-answer(Pkt, Req, _Peer, Name, #group{client_dict0 = Dict0}) ->
+answer(Pkt, Req, _Peer, Name, #group{client_dict = Dict0}) ->
#diameter_packet{header = H, msg = Ans, errors = Es} = Pkt,
ApplId = app(Req, Name, Dict0),
#diameter_header{application_id = ApplId} = H, %% assert
- Dict = dict(Ans, Dict0),
- [R | Vs] = Dict:'#get-'(answer(Ans, Es, Name)),
- [Dict:rec2msg(R) | Vs].
+ answer(Ans, Es, Name).
%% Missing Result-Code and inappropriate Experimental-Result-Code.
-answer(Rec, Es, send_experimental_result) ->
+answer(Ans, Es, send_experimental_result) ->
[{5004, #diameter_avp{name = 'Experimental-Result'}},
{5005, #diameter_avp{name = 'Result-Code'}}]
= Es,
- Rec;
+ Ans;
%% An inappropriate E-bit results in a decode error ...
-answer(Rec, Es, send_bad_answer) ->
+answer(Ans, Es, send_bad_answer) ->
[{5004, #diameter_avp{name = 'Result-Code'}} | _] = Es,
- Rec;
+ Ans;
%% ... while other errors are reflected in Failed-AVP.
-answer(Rec, [], _) ->
- Rec.
+answer(Ans, [], _) ->
+ Ans.
app(_, send_unsupported_app, _) ->
?BAD_APP;
@@ -1253,25 +1635,29 @@ app(Req, _, Dict0) ->
Dict = dict(Req, Dict0),
Dict:id().
-%% handle_error/6
+%% handle_error/7
-handle_error(timeout = Reason, _Req, [$C|_], _Peer, _, Time) ->
+handle_error(timeout = Reason, _Req, [$C|_], _Peer, _, _, Time) ->
Now = diameter_lib:now(),
{Reason, {diameter_lib:timestamp(Time),
diameter_lib:timestamp(Now),
diameter_lib:micro_diff(Now, Time)}};
-handle_error(Reason, _Req, [$C|_], _Peer, _, _Time) ->
+handle_error(Reason, _Req, [$C|_], _Peer, _, _, _Time) ->
{error, Reason}.
-%% handle_request/3
+%% handle_request/4
%% Note that diameter will set Result-Code and Failed-AVPs if
%% #diameter_packet.errors is non-null.
-handle_request(#diameter_packet{header = H, msg = M, avps = As},
+handle_request(#diameter_packet{header = H, avps = As}
+ = Pkt,
_,
- {_Ref, Caps}) ->
+ {_Ref, Caps},
+ #group{encoding = E,
+ server_decoding = D}
+ = Grp) ->
#diameter_header{end_to_end_id = EI,
hop_by_hop_id = HI}
= H,
@@ -1279,24 +1665,62 @@ handle_request(#diameter_packet{header = H, msg = M, avps = As},
V = EI bsr B, %% assert
V = HI bsr B, %%
#diameter_caps{origin_state_id = {_,[Id]}} = Caps,
- answer(origin(Id), request(M, [H|As], Caps)).
+ {D,E} = T = origin(Id), %% assert
+ wrap(T, H, request(to_map(Pkt, Grp), [H|As], Caps)).
+
+wrap(Id, H, {Tag, Action, Post}) ->
+ {Tag, wrap(Id, H, Action), Post};
-answer(T, {Tag, Action, Post}) ->
- {Tag, answer(T, Action), Post};
-answer(_, {reply, [#diameter_header{} | _]} = T) ->
+wrap(_, _, {reply, [#diameter_header{} | _]} = T) ->
T;
-answer({A,C}, {reply, Ans}) ->
- answer(C, {reply, msg(Ans, A, diameter_gen_base_rfc3588)});
-answer(pkt, {reply, Ans})
- when not is_record(Ans, diameter_packet) ->
- {reply, #diameter_packet{msg = Ans}};
-answer(_, T) ->
+
+wrap({_,E}, H, {reply, Ans}) ->
+ Msg = base_to_nas(msg(Ans, E, diameter_gen_base_rfc3588), H),
+ {reply, wrap(Msg)};
+
+wrap(_, _, T) ->
T.
+%% Randomly wrap the answer in a diameter_packet.
+
+wrap(#diameter_packet{} = Pkt) ->
+ Pkt;
+
+wrap(Msg) ->
+ case rand:uniform(2) of
+ 1 -> #diameter_packet{msg = Msg};
+ 2 -> Msg
+ end.
+
+%% base_to_nas/2
+
+base_to_nas(#diameter_packet{msg = Msg} = Pkt, H) ->
+ Pkt#diameter_packet{msg = base_to_nas(Msg, H)};
+
+base_to_nas(Rec, #diameter_header{application_id = 1})
+ when is_tuple(Rec), not ?is_record(Rec, 'diameter_base_answer-message') ->
+ D = case element(1, Rec) of
+ diameter_base_accounting_ACA ->
+ diameter_gen_base_accounting;
+ _ ->
+ diameter_gen_base_rfc3588
+ end,
+ [R | Values] = D:'#get-'(Rec),
+ "diameter_base_" ++ N = ?L(R),
+ Name = ?A("nas_" ++ if N == "accounting_ACA" ->
+ "ACA";
+ true ->
+ N
+ end),
+ nas4005:'#new-'([Name | Values]);
+
+base_to_nas(Msg, _) ->
+ Msg.
+
%% request/3
%% send_experimental_result
-request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 5},
+request(['ACR' | #{'Accounting-Record-Number' := 5}],
[Hdr | Avps],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
@@ -1329,14 +1753,14 @@ request(Msg, _Avps, Caps) ->
%% request/2
%% send_nok
-request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0},
+request(['ACR' | #{'Accounting-Record-Number' := 0}],
_) ->
{eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]};
%% send_bad_answer
-request(#diameter_base_accounting_ACR{'Session-Id' = SId,
- 'Accounting-Record-Type' = RT,
- 'Accounting-Record-Number' = 2 = RN},
+request(['ACR' | #{'Session-Id' := SId,
+ 'Accounting-Record-Type' := RT,
+ 'Accounting-Record-Number' := 2 = RN}],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
Ans = ['ACA', {'Result-Code', ?SUCCESS},
@@ -1350,9 +1774,9 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
msg = Ans}};
%% send_eval
-request(#diameter_base_accounting_ACR{'Session-Id' = SId,
- 'Accounting-Record-Type' = RT,
- 'Accounting-Record-Number' = 3 = RN},
+request(['ACR' | #{'Session-Id' := SId,
+ 'Accounting-Record-Type' := RT,
+ 'Accounting-Record-Number' := 3 = RN}],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
Ans = ['ACA', {'Result-Code', ?SUCCESS},
@@ -1364,9 +1788,9 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{eval, {reply, Ans}, {erlang, now, []}};
%% send_ok
-request(#diameter_base_accounting_ACR{'Session-Id' = SId,
- 'Accounting-Record-Type' = RT,
- 'Accounting-Record-Number' = 1 = RN},
+request(['ACR' | #{'Session-Id' := SId,
+ 'Accounting-Record-Type' := RT,
+ 'Accounting-Record-Number' := 1 = RN}],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, ['ACA', {'Result-Code', ?SUCCESS},
@@ -1377,48 +1801,69 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId,
{'Accounting-Record-Number', RN}]};
%% send_protocol_error
-request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4},
+request(['ACR' | #{'Accounting-Record-Number' := 4}],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
+ %% Include a DOIC AVP that will be encoded/decoded because of
+ %% avp_dictionaries config.
+ OLR = #{'OC-Sequence-Number' => 1,
+ 'OC-Report-Type' => 0, %% HOST_REPORT
+ 'OC-Reduction-Percentage' => [25],
+ 'OC-Validity-Duration' => [60],
+ 'AVP' => [{'OC-Supported-Features', []}]},
+ %% Include a NAS Failed-AVP AVP that will only be decoded under
+ %% that application. Encode as 'AVP' since RFC 3588 doesn't list
+ %% Failed-AVP in the answer-message grammar while RFC 6733 does.
+ NP = #diameter_avp{data = {nas4005, 'NAS-Port', 44}},
+ FR = #diameter_avp{name = 'Firmware-Revision', value = 12}, %% M=0
+ AP = #diameter_avp{name = 'Auth-Grace-Period', value = 13}, %% M=1
+ Failed = #diameter_avp{data = {diameter_gen_base_rfc3588,
+ 'Failed-AVP',
+ [{'AVP', [NP,FR,AP]}]}},
Ans = ['answer-message', {'Result-Code', ?TOO_BUSY},
{'Origin-Host', OH},
- {'Origin-Realm', OR}],
+ {'Origin-Realm', OR},
+ {'AVP', [{'OC-OLR', OLR}, Failed]}],
{reply, Ans};
-request(#diameter_base_ASR{'Session-Id' = SId,
- 'AVP' = Avps},
+%% send_proxy_info
+request(['ASR' | #{'Proxy-Info' := _}],
+ _) ->
+ {protocol_error, 3999};
+
+request(['ASR' | #{'Session-Id' := SId} = Avps],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, ['ASA', {'Result-Code', ?SUCCESS},
{'Session-Id', SId},
{'Origin-Host', OH},
{'Origin-Realm', OR},
- {'AVP', Avps}]};
+ {'AVP', maps:get('AVP', Avps, [])}]};
%% send_invalid_reject
-request(#diameter_base_STR{'Termination-Cause' = ?USER_MOVED},
+request(['STR' | #{'Termination-Cause' := ?USER_MOVED}],
_Caps) ->
{protocol_error, ?TOO_BUSY};
%% send_noreply
-request(#diameter_base_STR{'Termination-Cause' = T},
+request(['STR' | #{'Termination-Cause' := T}],
_Caps)
when T /= ?LOGOUT ->
discard;
%% send_destination_5
-request(#diameter_base_STR{'Destination-Realm' = R},
+request(['STR' | #{'Destination-Realm' := R}],
#diameter_caps{origin_realm = {OR, _}})
when R /= undefined, R /= OR ->
{protocol_error, ?REALM_NOT_SERVED};
%% send_destination_6
-request(#diameter_base_STR{'Destination-Host' = [H]},
+request(['STR' | #{'Destination-Host' := [H]}],
#diameter_caps{origin_host = {OH, _}})
when H /= OH ->
{protocol_error, ?UNABLE_TO_DELIVER};
-request(#diameter_base_STR{'Session-Id' = SId},
+request(['STR' | #{'Session-Id' := SId}],
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, ['STA', {'Result-Code', ?SUCCESS},
@@ -1427,5 +1872,61 @@ request(#diameter_base_STR{'Session-Id' = SId},
{'Origin-Realm', OR}]};
%% send_error/send_timeout
-request(#diameter_base_RAR{}, _Caps) ->
+request(['RAR' | #{}], _Caps) ->
receive after 2000 -> {protocol_error, ?TOO_BUSY} end.
+
+%% message/3
+%%
+%% Limit the number of messages received. More can be received if read
+%% in the same packet.
+
+message(recv = D, {[_], Bin}, N) ->
+ message(D, Bin, N);
+message(Dir, #diameter_packet{bin = Bin}, N) ->
+ message(Dir, Bin, N);
+
+%% incoming request
+message(recv, <<_:32, 1:1, _/bits>> = Bin, N) ->
+ [Bin, N < 16, fun ?MODULE:message/3, N+1];
+
+%% incoming answer
+message(recv, Bin, _) ->
+ [Bin];
+
+%% outgoing
+message(send, Bin, _) ->
+ [Bin];
+
+%% sent request
+message(ack, <<_:32, 1:1, _/bits>>, _) ->
+ [];
+
+%% sent answer or discarded request
+message(ack, _, N) ->
+ [N =< 16, fun ?MODULE:message/3, N-1].
+
+%% ------------------------------------------------------------------------
+
+compile_and_load() ->
+ try
+ Path = hd([P || H <- [[here(), ".."], [code:lib_dir(diameter)]],
+ P <- [filename:join(H ++ ["examples",
+ "dict",
+ "rfc4005_nas.dia"])],
+ {ok, _} <- [file:read_file_info(P)]]),
+ {ok, [Forms]}
+ = diameter_make:codec(Path, [return,
+ forms,
+ {name, "nas4005"},
+ {prefix, "nas"},
+ {inherits, "common/diameter_gen_base_rfc3588"}]),
+ {ok, nas4005, Bin, []} = compile:forms(Forms, [debug_info, return]),
+ {module, nas4005} = code:load_binary(nas4005, "nas4005", Bin),
+ true
+ catch
+ E:R:Stack ->
+ {E, R, Stack}
+ end.
+
+here() ->
+ filename:dirname(code:which(?MODULE)).
diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl
index c94f46b7a5..284d2b9566 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-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -294,10 +294,17 @@ init(gen_accept, {Prot, Ref}) ->
{ok, PortNr} = inet:port(LSock),
true = diameter_reg:add_new(?TEST_LISTENER(Ref, PortNr)),
- %% Accept a connection, receive a message and send it back.
+ %% Accept a connection, receive a message send it back, and wait
+ %% for the peer to close the connection.
{ok, Sock} = gen_accept(Prot, LSock),
Bin = gen_recv(Prot, Sock),
- ok = gen_send(Prot, Sock, Bin);
+ ok = gen_send(Prot, Sock, Bin),
+ receive
+ {tcp_closed, Sock} = T ->
+ T;
+ ?SCTP(Sock, {_, #sctp_assoc_change{}}) = T ->
+ T
+ end;
init(connect, {Prot, Ref}) ->
%% Lookup the peer's listening socket.
@@ -311,12 +318,7 @@ init(connect, {Prot, Ref}) ->
%% Send a message and receive it back.
Bin = make_msg(),
TPid ! ?TMSG({send, Bin}),
- Bin = bin(Prot, ?RECV(?TMSG({recv, P}), P)),
-
- %% Expect the transport process to die as a result of the peer
- %% closing the connection.
- MRef = erlang:monitor(process, TPid),
- ?RECV({'DOWN', MRef, process, _, _}).
+ Bin = bin(Prot, ?RECV(?TMSG({recv, P}), P)).
bin(sctp, #diameter_packet{bin = Bin}) ->
Bin;
@@ -336,50 +338,51 @@ make_msg() ->
<<1:8, Len:24, Bin/binary>>.
%% crypto:rand_bytes/1 isn't available on all platforms (since openssl
-%% isn't) so roll our own.
+%% isn't) so roll our own. Not particularly random, but less verbose
+%% in trace.
rand_bytes(N) ->
- rand_bytes(N, <<>>).
-
-rand_bytes(0, Bin) ->
- Bin;
-rand_bytes(N, Bin) ->
Oct = rand:uniform(256) - 1,
- rand_bytes(N-1, <<Oct, Bin/binary>>).
+ binary:copy(<<Oct>>, N).
%% ===========================================================================
%% start_connect/3
start_connect(Prot, PortNr, Ref) ->
- {ok, TPid, [?ADDR]} = start_connect(Prot,
- {connect, Ref},
- ?SVC([]),
- [{raddr, ?ADDR},
- {rport, PortNr},
- {ip, ?ADDR},
- {port, 0}]),
- ?RECV(?TMSG({TPid, connected, _})),
+ {ok, TPid} = start_connect(Prot,
+ {connect, Ref},
+ ?SVC([]),
+ [{raddr, ?ADDR},
+ {rport, PortNr},
+ {ip, ?ADDR},
+ {port, 0}]),
+ connected(Prot, TPid),
TPid.
+connected(sctp, TPid) ->
+ ?RECV(?TMSG({TPid, connected, _}));
+connected(tcp, TPid) ->
+ ?RECV(?TMSG({TPid, connected, _, [?ADDR]})).
+
start_connect(sctp, T, Svc, Opts) ->
- diameter_sctp:start(T, Svc, [{sctp_initmsg, ?SCTP_INIT} | Opts]);
+ {ok, TPid, [?ADDR]}
+ = diameter_sctp:start(T, Svc, [{sctp_initmsg, ?SCTP_INIT} | Opts]),
+ {ok, TPid};
start_connect(tcp, T, Svc, Opts) ->
diameter_tcp:start(T, Svc, Opts).
%% start_accept/2
start_accept(Prot, Ref) ->
- {Mod, Opts} = tmod(Prot),
- {ok, TPid, [?ADDR]} = Mod:start({accept, Ref},
- ?SVC([?ADDR]),
- [{port, 0} | Opts]),
+ {ok, TPid, [?ADDR]}
+ = start_accept(Prot, {accept, Ref}, ?SVC([?ADDR]), [{port, 0}]),
?RECV(?TMSG({TPid, connected})),
TPid.
-tmod(sctp) ->
- {diameter_sctp, [{sctp_initmsg, ?SCTP_INIT}]};
-tmod(tcp) ->
- {diameter_tcp, []}.
+start_accept(sctp, T, Svc, Opts) ->
+ diameter_sctp:start(T, Svc, [{sctp_initmsg, ?SCTP_INIT} | Opts]);
+start_accept(tcp, T, Svc, Opts) ->
+ diameter_tcp:start(T, Svc, Opts).
%% ===========================================================================
diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl
index cca28dd23c..d249b0e4fa 100644
--- a/lib/diameter/test/diameter_util.erl
+++ b/lib/diameter/test/diameter_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,7 +32,8 @@
foldl/3,
scramble/1,
unique_string/0,
- have_sctp/0]).
+ have_sctp/0,
+ eprof/1]).
%% diameter-specific
-export([lport/2,
@@ -48,6 +49,16 @@
-define(L, atom_to_list).
+%% ---------------------------------------------------------------------------
+
+eprof(start) ->
+ eprof:start(),
+ eprof:start_profiling([self()]);
+
+eprof(stop) ->
+ eprof:stop_profiling(),
+ eprof:analyze(),
+ eprof:stop().
%% ---------------------------------------------------------------------------
%% name/2
@@ -172,18 +183,7 @@ recvl([{MRef, F} | L], Ref, Fun, Acc) ->
%% Sort a list into random order.
scramble(L) ->
- foldl(fun(true, _, S, false) -> S end,
- false,
- [[fun s/1, L]]).
-
-s(L) ->
- s([], L).
-
-s(Acc, []) ->
- Acc;
-s(Acc, L) ->
- {H, [T|Rest]} = lists:split(rand:uniform(length(L)) - 1, L),
- s([T|Acc], H ++ Rest).
+ [X || {_,X} <- lists:sort([{rand:uniform(), T} || T <- L])].
%% ---------------------------------------------------------------------------
%% unique_string/0
@@ -195,21 +195,22 @@ unique_string() ->
%% have_sctp/0
have_sctp() ->
- case erlang:system_info(system_architecture) of
- %% We do not support the sctp version present in solaris
- %% version "sparc-sun-solaris2.10", that behaves differently
- %% from later versions and linux
- "sparc-sun-solaris2.10" ->
- false;
- _->
- case gen_sctp:open() of
- {ok, Sock} ->
- gen_sctp:close(Sock),
- true;
- {error, E} when E == eprotonosupport;
- E == esocktnosupport -> %% fail on any other reason
- false
- end
+ have_sctp(erlang:system_info(system_architecture)).
+
+%% Don't run SCTP on platforms where it's either known to be flakey or
+%% isn't available.
+
+have_sctp("sparc-sun-solaris2.10") ->
+ false;
+
+have_sctp(_) ->
+ case gen_sctp:open() of
+ {ok, Sock} ->
+ gen_sctp:close(Sock),
+ true;
+ {error, E} when E == eprotonosupport;
+ E == esocktnosupport -> %% fail on any other reason
+ false
end.
%% ---------------------------------------------------------------------------
@@ -313,17 +314,23 @@ listen(SvcName, Prot, Opts) ->
connect(Client, Prot, LRef) ->
connect(Client, Prot, LRef, []).
-connect(Client, Prot, LRef, Opts) ->
+connect(Client, ProtOpts, LRef, Opts) ->
+ Prot = head(ProtOpts),
[PortNr] = lport(Prot, LRef),
Client = diameter:service_info(Client, name), %% assert
true = diameter:subscribe(Client),
- Ref = add_transport(Client, {connect, opts(Prot, PortNr) ++ Opts}),
+ Ref = add_transport(Client, {connect, opts(ProtOpts, PortNr) ++ Opts}),
true = transport(Client, Ref), %% assert
diameter_lib:for_n(fun(_) -> ok = up(Client, Ref, Prot, PortNr) end,
proplists:get_value(pool_size, Opts, 1)),
Ref.
+head([T|_]) ->
+ T;
+head(T) ->
+ T.
+
up(Client, Ref, Prot, PortNr) ->
receive
{diameter_event, Client, {up, Ref, _, _, _}} -> ok
@@ -366,10 +373,13 @@ tmod(sctp) ->
tmod(any) ->
[diameter_sctp, diameter_tcp].
-opts(Prot, T) ->
- tmo(T, lists:append([[{transport_module, M}, {transport_config, C}]
+opts([Prot | Opts], T) ->
+ tmo(T, lists:append([[{transport_module, M}, {transport_config, C ++ Opts}]
|| M <- tmod(Prot),
- C <- [cfg(M,T) ++ cfg(M) ++ cfg(T)]])).
+ C <- [cfg(M,T) ++ cfg(M) ++ cfg(T)]]));
+
+opts(Prot, T) ->
+ opts([Prot], T).
tmo(listen, Opts) ->
Opts;
diff --git a/lib/diameter/test/diameter_watchdog_SUITE.erl b/lib/diameter/test/diameter_watchdog_SUITE.erl
index 6d22ddcc18..f3f168e671 100644
--- a/lib/diameter/test/diameter_watchdog_SUITE.erl
+++ b/lib/diameter/test/diameter_watchdog_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -44,13 +44,8 @@
-export([peer_up/3,
peer_down/3]).
-%% gen_tcp-ish interface
--export([listen/2,
- accept/1,
- connect/3,
- send/2,
- setopts/2,
- close/1]).
+%% diameter_tcp message_cb
+-export([message/3]).
-include("diameter.hrl").
-include("diameter_ct.hrl").
@@ -161,9 +156,9 @@ reopen(Type, Test, Ref, Wd, N, M) ->
reopen(Type, Test, SvcName, TRef, Wd, N, M).
cfg(Type, Type, Wd) ->
- {Wd, [], []};
+ {Wd, [], false};
cfg(_Type, _Test, _Wd) ->
- {?WD(?PEER_WD), [{okay, 0}], [{module, ?MODULE}]}.
+ {?WD(?PEER_WD), [{okay, 0}], true}.
%% reopen/7
@@ -346,7 +341,7 @@ recv_reopen(listen, Ref) ->
%% reg/3
%%
%% Lookup the pid of the transport process and publish a term for
-%% send/2 to lookup.
+%% message/3 to lookup.
reg(TRef, SvcName, T) ->
TPid = tpid(TRef, diameter:service_info(SvcName, transport)),
true = diameter_reg:add_new({?MODULE, TPid, T}).
@@ -394,7 +389,7 @@ suspect(_) ->
suspect(Type, Fake, Ref, N)
when is_reference(Ref) ->
{SvcName, TRef}
- = start(Type, Ref, {?WD(10000), [{suspect, N}], mod(Fake)}),
+ = start(Type, Ref, {?WD(10000), [{suspect, N}], Fake}),
{initial, okay} = ?WD_EVENT(TRef),
suspect(TRef, Fake, SvcName, N);
@@ -436,11 +431,6 @@ abuse([F|A], Test) ->
abuse(F, Test) ->
abuse([F], Test).
-mod(true) ->
- [{module, ?MODULE}];
-mod(false) ->
- [].
-
%% ===========================================================================
%% # okay/1
%% ===========================================================================
@@ -456,7 +446,7 @@ okay(Type, Fake, Ref, N)
{SvcName, TRef}
= start(Type, Ref, {?WD(10000),
[{okay, choose(Fake, 0, N)}],
- mod(Fake)}),
+ Fake}),
{initial, okay} = ?WD_EVENT(TRef),
okay(TRef,
Fake,
@@ -515,12 +505,17 @@ start(Type, Ref, T) ->
true = diameter_reg:add_new({Type, Ref, Name}),
{Name, TRef}.
-opts(Type, Ref, {Timer, Config, Mod}) ->
+opts(Type, Ref, {Timer, Config, Fake})
+ when is_boolean(Fake) ->
[{transport_module, diameter_tcp},
- {transport_config, Mod ++ [{ip, ?ADDR}, {port, 0}] ++ cfg(Type, Ref)},
+ {transport_config, mod(Fake) ++ [{ip, ?ADDR}, {port, 0}]
+ ++ cfg(Type, Ref)},
{watchdog_timer, Timer},
{watchdog_config, Config}].
+mod(B) ->
+ [{message_cb, [fun message/3, capx]} || B].
+
cfg(listen, _) ->
[];
cfg(connect, Ref) ->
@@ -531,37 +526,29 @@ cfg(connect, Ref) ->
%% ===========================================================================
-listen(PortNr, Opts) ->
- gen_tcp:listen(PortNr, Opts).
-
-accept(LSock) ->
- gen_tcp:accept(LSock).
+%% message/3
-connect(Addr, Port, Opts) ->
- gen_tcp:connect(Addr, Port, Opts).
+message(send, Bin, X) ->
+ send(Bin, X);
-setopts(Sock, Opts) ->
- inet:setopts(Sock, Opts).
+message(recv, Bin, _) ->
+ [Bin];
-send(Sock, Bin) ->
- send(getr(config), Sock, Bin).
-
-close(Sock) ->
- gen_tcp:close(Sock).
+message(_, _, _) ->
+ [].
-%% send/3
+%% send/2
%% First outgoing message from a new transport process is CER/CEA.
%% Remaining outgoing messages are either DWR or DWA.
-send(undefined, Sock, Bin) ->
- <<_:32, _:8, 257:24, _/binary>> = Bin,
- putr(config, init),
- gen_tcp:send(Sock, Bin);
+send(Bin, capx) ->
+ <<_:32, _:8, 257:24, _/binary>> = Bin, %% assert on CER/CEA
+ [Bin, fun message/3, init];
%% Outgoing DWR: fake reception of DWA. Use the fact that AVP values
%% are ignored. This is to ensure that the peer's watchdog state
%% transitions are only induced by responses to messages it sends.
-send(_, Sock, <<_:32, 1:1, _:7, 280:24, _:32, EId:32, HId:32, _/binary>>) ->
+send(<<_:32, 1:1, _:7, 280:24, _:32, EId:32, HId:32, _/binary>>, _) ->
Pkt = #diameter_packet{header = #diameter_header{version = 1,
end_to_end_id = EId,
hop_by_hop_id = HId},
@@ -569,47 +556,36 @@ send(_, Sock, <<_:32, 1:1, _:7, 280:24, _:32, EId:32, HId:32, _/binary>>) ->
{'Origin-Host', "XXX"},
{'Origin-Realm', ?REALM}]},
#diameter_packet{bin = Bin} = diameter_codec:encode(?BASE, Pkt),
- self() ! {tcp, Sock, Bin},
- ok;
+ [recv, Bin];
%% First outgoing DWA.
-send(init, Sock, Bin) ->
+send(Bin, init) ->
[{{?MODULE, _, T}, _}] = diameter_reg:wait({?MODULE, self(), '_'}),
- putr(config, T),
- send(Sock, Bin);
+ send(Bin, T);
%% First transport process.
-send({SvcName, {_,_,_} = T}, Sock, Bin) ->
+send(Bin, {SvcName, {_,_,_} = T}) ->
[{'Origin-Host', _} = OH, {'Origin-Realm', _} = OR | _]
= ?SERVICE(SvcName),
putr(origin, [OH, OR]),
- putr(config, T),
- send(Sock, Bin);
+ send(Bin, T);
%% Discard DWA, failback after another timeout in the peer.
-send({Wd, 0 = No, Msg}, Sock, Bin) ->
+send(Bin, {Wd, 0 = No, Msg}) ->
Origin = getr(origin),
- spawn(fun() -> failback(?ONE_WD(Wd), Msg, Sock, Bin, Origin) end),
- putr(config, No),
- ok;
+ [{defer, ?ONE_WD(Wd), [msg(Msg, Bin, Origin)]}, fun message/3, No];
%% Send DWA while we're in the mood (aka 0 < N).
-send({Wd, N, Msg}, Sock, Bin) ->
- putr(config, {Wd, N-1, Msg}),
- gen_tcp:send(Sock, Bin);
+send(Bin, {Wd, N, Msg}) ->
+ [Bin, fun message/3, {Wd, N-1, Msg}];
%% Discard DWA.
-send(0, _Sock, _Bin) ->
- ok;
+send(_Bin, 0 = No) ->
+ [fun message/3, No];
%% Send DWA.
-send(N, Sock, <<_:32, 0:1, _:7, 280:24, _/binary>> = Bin) ->
- putr(config, N-1),
- gen_tcp:send(Sock, Bin).
-
-failback(Tmo, Msg, Sock, Bin, Origin) ->
- timer:sleep(Tmo),
- ok = gen_tcp:send(Sock, msg(Msg, Bin, Origin)).
+send(<<_:32, 0:1, _:7, 280:24, _/binary>> = DWA, N) ->
+ [DWA, fun message/3, N-1].
%% msg/2
@@ -662,10 +638,9 @@ run1([F|A]) ->
apply(?MODULE, F, A),
ok
catch
- E:R ->
- S = erlang:get_stacktrace(),
- ?WARN("~p", [{A, E, R, S}]),
- S
+ E:R:Stack ->
+ ?WARN("~p", [{A, E, R, Stack}]),
+ Stack
end.
%% jitter/2
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index 80d0f8d59c..0c73adca12 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -1,7 +1,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2015. All Rights Reserved.
+# Copyright Ericsson AB 2010-2017. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -31,7 +31,6 @@ MODULES = \
diameter_codec_test \
diameter_config_SUITE \
diameter_compiler_SUITE \
- diameter_dict_SUITE \
diameter_distribution_SUITE \
diameter_dpr_SUITE \
diameter_event_SUITE \
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 94d9d72a48..8c75c9e55e 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -1,6 +1,6 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2010-2017. All Rights Reserved.
+# Copyright Ericsson AB 2010-2018. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,5 +17,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.12.2
+DIAMETER_VSN = 2.1.6
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)