diff options
Diffstat (limited to 'lib/diameter')
35 files changed, 3528 insertions, 707 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index ad82cafd2f..6b84b22eb5 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -397,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 @@ -418,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> @@ -484,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> @@ -650,7 +650,7 @@ Result = ResultCode | {capabilities_cb, CB, ResultCode|discard} Caps = #diameter_caps{} Pkt = #diameter_packet{} ResultCode = integer() -CB = &evaluable; +CB = &eval; </pre> <p> @@ -799,7 +799,7 @@ be matched by corresponding &capability; configuration, of <tag> <marker id="decode_format"/> -<c>{decode_format, record | list | map | false}</c></tag> +<c>{decode_format, record | list | map | none}</c></tag> <item> <p> The format of decoded messages and grouped AVPs in the <c>msg</c> field @@ -808,10 +808,10 @@ 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 either a list of AVP name/values pairs or a map keyed on +<c>Avps</c> is a list of AVP name/values pairs or a map keyed on AVP names respectively. -If <c>false</c> then the representation is omitted and <c>msg</c> and -<c>value</c> fields are set to <c>false</c>. +If <c>none</c> then the atom-value message name, or <c>undefined</c> +for a Grouped AVP. See also &codec_message;.</p> <p> @@ -826,24 +826,11 @@ field of diameter_packet records independently of </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><c>{restrict_connections, false | node | nodes | [node()] - | evaluable()}</c></tag> + | eval()}</c></tag> <item> <p> The degree to which the service allows multiple transport @@ -854,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. @@ -869,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 @@ -904,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 @@ -917,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. @@ -943,18 +930,6 @@ 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_arities"/><c>{strict_arities, boolean() | encode @@ -988,49 +963,6 @@ of arity 1 as bare values, not wrapped in a list.</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> <marker id="string_decode"/><c>{string_decode, boolean()}</c></tag> <item> <p> @@ -1074,7 +1006,7 @@ omitted counters are not returned by &service_info;.</p> </item> -<tag><c>{use_shared_peers, boolean() | [node()] | evaluable()}</c></tag> +<tag><c>{use_shared_peers, boolean() | [node()] | eval()}</c></tag> <item> <p> Nodes from which communicated peers are made available in @@ -1084,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>. @@ -1109,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"/> @@ -1142,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> @@ -1156,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 @@ -1250,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 @@ -1350,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> @@ -1407,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> 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 0846334d23..5124b49484 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; @@ -145,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> @@ -243,8 +247,7 @@ 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 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_service_opt; -<c>decode_format</c>. +The format at decode is determined by &mod_decode_format;. Any of the formats is accepted at encode.</p> <p> @@ -288,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_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml index 9b6d629f79..c9b74a9ec5 100644 --- a/lib/diameter/doc/src/diameter_sctp.xml +++ b/lib/diameter/doc/src/diameter_sctp.xml @@ -16,7 +16,7 @@ <header> <copyright> <year>2011</year> -<year>2016</year> +<year>2017</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -116,7 +116,6 @@ 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> diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml index 6ca280c52b..1d65d14257 100644 --- a/lib/diameter/doc/src/diameter_tcp.xml +++ b/lib/diameter/doc/src/diameter_tcp.xml @@ -170,14 +170,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/seealso.ent b/lib/diameter/doc/src/seealso.ent index e5c284c6e8..c5a53670d0 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,6 +72,7 @@ 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 --> 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 + + Email: [email protected] + + + Steve Donovan (editor) + Oracle + 7460 Warren Parkway + Frisco, Texas 75034 + United States + + Email: [email protected] + + + Ben Campbell + Oracle + 7460 Warren Parkway + Frisco, Texas 75034 + United States + + Email: [email protected] + + + Lionel Morand + Orange Labs + 38/40 rue du General Leclerc + Issy-Les-Moulineaux Cedex 9 92794 + France + + Phone: +33145296257 + Email: [email protected] + + + + + + + + + + + + + + +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..fc5830f8e2 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-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. @@ -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/src/Makefile b/lib/diameter/src/Makefile index 6bf748a727..3af856f63e 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-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. @@ -274,9 +274,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/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 2e18a1d903..b90b794611 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -46,7 +46,8 @@ -export([start/0, stop/0]). --export_type([evaluable/0, +-export_type([eval/0, + evaluable/0, %% deprecated decode_format/0, strict_arities/0, restriction/0, @@ -301,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()]}. @@ -309,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}. @@ -322,12 +326,12 @@ call(SvcName, App, Message) -> | node | nodes | [node()] - | evaluable(). + | eval(). -type remotes() :: boolean() | [node()] - | evaluable(). + | eval(). -type message_length() :: 0..16#FFFFFF. @@ -336,7 +340,7 @@ call(SvcName, App, Message) -> :: record | list | map - | false + | none | record_from_map. -type strict_arities() @@ -344,22 +348,39 @@ call(SvcName, App, Message) -> | 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_arities, true | strict_arities()} - | {strict_mbit, boolean()} - | {incoming_maxlen, message_length()} | {use_shared_peers, remotes()} - | {spawn_opt, list()}. + | common_opt(). -type application_opt() :: {alias, app_alias()} @@ -389,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 diff --git a/lib/diameter/src/base/diameter_callback.erl b/lib/diameter/src/base/diameter_callback.erl index f9cdc66c70..d04a416bef 100644 --- a/lib/diameter/src/base/diameter_callback.erl +++ b/lib/diameter/src/base/diameter_callback.erl @@ -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 preceding 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_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 63e39b12d1..2dd2c906a2 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -324,7 +324,7 @@ 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#{dictionary => AppMod, + Opts#{app_dictionary => AppMod, failed_avp => false}), ?LOGC([] /= Errors, decode_errors, Pkt#diameter_packet.header), Pkt#diameter_packet{msg = reformat(MsgName, Rec, Opts), @@ -614,8 +614,8 @@ pack_avp(#diameter_avp{data = {T, {Type, Value}}}, Opts) -> pack_avp(#diameter_avp{data = {T, Data}}, _) -> pack_data(T, Data); -pack_avp(#diameter_avp{data = {Dict, Name, Data}}, Opts) -> - pack_data(Dict:avp_header(Name), Dict:avp(encode, Data, Name, Opts)); +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}, _) diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index f1b6e56782..90a9282349 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -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). @@ -560,87 +557,186 @@ add(SvcName, Type, Opts0) -> end. transport_opts(Opts) -> - lists:map(fun topt/1, Opts). + [setopt(transport, T) || T <- Opts]. + +%% setopt/2 -topt(T) -> - case opt(T) of +setopt(K, T) -> + case opt(K, T) of {value, X} -> X; true -> T; false -> - ?THROW({invalid, T}) + ?THROW({invalid, T}); + {error, Reason} -> + ?THROW({invalid, T, Reason}) end. -opt({transport_module, M}) -> +%% 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(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 -> + {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 -> + {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, {M,F,A}}) +opt(_, {spawn_opt, {M,F,A}}) when is_atom(M), is_atom(F), is_list(A) -> true; -opt({spawn_opt = K, Opts}) -> +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 @@ -705,19 +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_arities}, - {true, strict_mbit}, - {record, decode_format}, - {true, traffic_counters}, - {true, string_decode}, - {[], spawn_opt}]), + SvcOpts = service_opts((Opts -- AppOpts) -- CapOpts), D = proplists:get_value(string_decode, SvcOpts, true), @@ -731,115 +815,22 @@ binary_caps(Caps, true) -> binary_caps(Caps, false) -> diameter_capx:binary_caps(Caps). -%% make_opts/2 +%% service_opts/1 -make_opts(Opts, Defs) -> - Known = [{K, get_opt(K, Opts, D)} || {D,K} <- Defs], - Unknown = Opts -- Known, - - [] == Unknown orelse ?THROW({invalid, hd(Unknown)}), - - [{K, opt(K,V)} || {K,V} <- Known]. - -opt(incoming_maxlen, N) - when 0 =< N, N < 1 bsl 24 -> - N; - -opt(spawn_opt, {M,F,A} = T) - when is_atom(M), is_atom(F), is_list(A) -> - T; - -opt(spawn_opt, L) - when is_list(L) -> - spawn_opts(L); - -opt(K, false = B) - when K == share_peers; - K == use_shared_peers; - K == monitor; - K == restrict_connections; - K == strict_arities; - K == strict_mbit; - K == decode_format; - K == traffic_counters; - K == string_decode -> - B; - -opt(K, true = B) - when K == share_peers; - K == use_shared_peers; - K == strict_arities; - K == strict_mbit; - K == traffic_counters; - K == string_decode -> - B; - -opt(decode_format, T) - when T == record; - T == list; - T == map; - T == record_from_map -> - T; - -opt(strict_arities, T) - when T == encode; - T == decode -> - T; - -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}). +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. spawn_opts(L) -> [T || T <- L, T /= link, T /= monitor]. -sequence({H,N} = T) - when 0 =< N, N =< 32, 0 =< H, 0 == H bsr (32-N) -> - T; - -sequence(_) -> - ?THROW({value, sequence}). - make_caps(Caps, Opts) -> case diameter_capx:make_caps(Caps, Opts) of {ok, T} -> diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index f9172ec59d..d3b9f704fe 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -45,7 +45,7 @@ -define(THROW(T), throw({?MODULE, T})). -type parent_name() :: atom(). %% parent = Message or AVP --type parent_record() :: tuple(). %% +-type parent_record() :: tuple() | avp_values() | map(). -type avp_name() :: atom(). -type avp_record() :: tuple(). -type avp_values() :: [{avp_name(), term()}]. @@ -61,9 +61,7 @@ %% # encode_avps/3 %% --------------------------------------------------------------------------- --spec encode_avps(parent_name(), - parent_record() | avp_values() | map(), - map()) +-spec encode_avps(parent_name(), parent_record(), map()) -> iolist() | no_return(). @@ -102,73 +100,73 @@ encode(Name, Vals, Opts, Strict, Mod) encode(Name, Map, Opts, Strict, Mod) when is_map(Map) -> - [enc(Name, F, A, V, Opts, Strict, Mod) || {F,A} <- Mod:avp_arity(Name), - V <- [mget(F, Map, undefined)]]; + [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(Name, AvpName, Values, Opts, Strict, Mod) +encode(_, AvpName, Values, Opts, Strict, Mod) when Strict /= encode -> - enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod); + enc(AvpName, ?ANY, Values, Opts, Strict, Mod); encode(Name, AvpName, Values, Opts, Strict, Mod) -> Arity = Mod:avp_arity(Name, AvpName), - enc(Name, AvpName, Arity, Values, Opts, Strict, Mod). + enc(AvpName, Arity, Values, Opts, Strict, Mod). -%% enc/7 +%% enc/6 -enc(Name, AvpName, Arity, Values, Opts, Strict, Mod) +enc(AvpName, Arity, Values, Opts, Strict, Mod) when Strict /= encode, Arity /= ?ANY -> - enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod); + enc(AvpName, ?ANY, Values, Opts, Strict, Mod); -enc(_, AvpName, 1, undefined, _, _, _) -> +enc(AvpName, 1, undefined, _, _, _) -> ?THROW([mandatory_avp_missing, AvpName]); -enc(Name, AvpName, 1, Value, Opts, _, Mod) -> +enc(AvpName, 1, Value, Opts, _, Mod) -> H = avp_header(AvpName, Mod), - enc1(Name, AvpName, H, Value, Opts, Mod); + enc(AvpName, H, Value, Opts, Mod); -enc(_, _, {0,_}, [], _, _, _) -> +enc(_, {0,_}, [], _, _, _) -> []; -enc(_, _, _, undefined, _, _, _) -> +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(Name, AvpName, Arity, V, Opts, Strict, Mod) +enc(AvpName, Arity, V, Opts, Strict, Mod) when not is_list(V) -> - enc(Name, AvpName, Arity, [V], Opts, Strict, Mod); + enc(AvpName, Arity, [V], Opts, Strict, Mod); -enc(Name, AvpName, {Min, Max}, Values, Opts, Strict, Mod) -> +enc(AvpName, {Min, Max}, Values, Opts, Strict, Mod) -> H = avp_header(AvpName, Mod), - enc(Name, AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod). + enc(AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod). -%% enc/10 +%% enc/9 -enc(Name, AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod) +enc(AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod) when Strict /= encode; Max == '*', Min =< N -> - [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; + [enc(AvpName, H, V, Opts, Mod) || V <- Vs]; -enc(_, AvpName, _, Min, N, _, [], _, _, _) +enc(AvpName, _, Min, N, _, [], _, _, _) when N < Min -> ?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]); -enc(_, _, _, _, _, _, [], _, _, _) -> +enc(_, _, _, _, _, [], _, _, _) -> []; -enc(_, AvpName, _, _, N, Max, _, _, _, _) +enc(AvpName, _, _, N, Max, _, _, _, _) when Max =< N -> ?THROW([repeated_avp_excessive_arity, AvpName, Max]); -enc(Name, AvpName, H, Min, N, Max, [V|Vs], Opts, Strict, Mod) -> - [enc1(Name, AvpName, H, V, Opts, Mod) - | enc(Name, AvpName, H, Min, N+1, Max, Vs, Opts, Strict, Mod)]. +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 @@ -178,12 +176,12 @@ avp_header('AVP', _) -> avp_header(AvpName, Mod) -> {_,_,_} = Mod:avp_header(AvpName). -%% enc1/6 +%% enc/5 -enc1(Name, 'AVP', false, Value, Opts, Mod) -> - enc_AVP(Name, Value, Opts, Mod); +enc('AVP', false, Value, Opts, Mod) -> + enc_AVP(Value, Opts, Mod); -enc1(_, AvpName, Hdr, Value, Opts, Mod) -> +enc(AvpName, Hdr, Value, Opts, Mod) -> enc1(AvpName, Hdr, Value, Opts, Mod). %% enc1/5 @@ -191,48 +189,66 @@ enc1(_, AvpName, Hdr, Value, Opts, Mod) -> enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod) -> diameter_codec:pack_data(Hdr, Mod:avp(encode, Value, AvpName, Opts)). -%% enc_AVP/4 +%% 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, _) -> +enc_AVP(#diameter_avp{value = undefined} = A, Opts, _) -> diameter_codec:pack_avp(A, Opts); -%% Missing name for value encode. -enc_AVP(_, #diameter_avp{name = N, value = V}, _, _) - when N == undefined; - N == 'AVP' -> - ?THROW([value_with_nameless_avp, N, V]); +%% 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 not. Ensure that 'AVP' is the appropriate field. Note that if we -%% don't know this AVP at all then the encode will fail. -enc_AVP(Name, #diameter_avp{name = AvpName, value = Data}, Opts, Mod) -> - 0 == Mod:avp_arity(Name, AvpName) - orelse ?THROW([known_avp_as_AVP, Name, AvpName, Data]), - enc(AvpName, Data, Opts, Mod); +%% ... or with a specified dictionary. +enc_AVP({Dict, AvpName, Value}, Opts, Mod) -> + enc1(AvpName, Dict:avp_header(AvpName), Value, Opts, Mod, Dict). -%% The backdoor ... -enc_AVP(_, {AvpName, Value}, Opts, Mod) -> - enc(AvpName, Value, Opts, Mod); +%% Don't guard against anything being sent as a generic 'AVP', which +%% allows arity restrictions to be abused. -%% ... and the side door. -enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> - diameter_codec:pack_avp(#diameter_avp{data = T}, Opts). +%% enc_AVP/4 -%% enc/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(AvpName, Value, Opts, Mod) -> - enc1(AvpName, Mod:avp_header(AvpName), Value, Opts, Mod). +%% 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(), [avp()], Failed} + -> {parent_record() | parent_name(), [avp()], Failed} when Failed :: [{5000..5999, #diameter_avp{}}]. decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) -> @@ -303,9 +319,9 @@ decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, type = type(NameT), index = Idx}, - Dec = decode(Data, Name, NameT, Mod, Opts, Avp), %% decode + Dec = dec(Data, Name, NameT, Mod, Fmt, Opts, Avp), Acc = decode(T, Name, Mod, Fmt, Strict, Opts, Idx+1, AM),%% recurse - acc(Acc, Dec, I, Name, Field, Arity, Strict, Mod, Opts); + acc(Acc, Dec, I, Field, Arity, Strict, Mod, Opts); _ -> {NameT, _Field, _Arity, {_, AM}} = incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0), @@ -451,11 +467,12 @@ field({AvpName, _}) -> field(_) -> 'AVP'. -%% decode/6 +%% dec/7 -%% AVP not in dictionary. -decode(_Data, _Name, 'AVP', _Mod, _Opts, Avp) -> - Avp; +%% 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: %% @@ -504,42 +521,106 @@ decode(_Data, _Name, 'AVP', _Mod, _Opts, Avp) -> %% defined the RFC's "unrecognized", which is slightly stronger than %% "not defined".) -decode(Data, Name, {AvpName, Type}, Mod, Opts, Avp) -> - #{dictionary := AppMod, failed_avp := Failed} +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. - DecMod = if Failed -> AppMod; - true -> Mod - end, - - %% 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 avp_decode(Data, AvpName, Opts, DecMod, Mod) of - {Rec, As} when Type == 'Grouped' -> - A = Avp#diameter_avp{value = Rec}, - [A | As]; - V when Type /= 'Grouped' -> - Avp#diameter_avp{value = V} + 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, T, Avp); + decode_error(Failed, Fmt, T, Avp); error: Reason -> decode_error(Failed, Reason, Name, Mod, Opts, Avp) end. -%% decode_error/3 +%% 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, Opts, Code, Vid, Avp) -> + case Dict:avp_name(Code, Vid) of + {AvpName, Type} -> + A = Avp#diameter_avp{name = AvpName, + type = Type}, + #{failed_avp := Failed} = Opts, + dec(Data, Name, AvpName, Type, Mod, Dict, Fmt, Failed, Opts, A); + _ -> + dec_AVP(Rest, Data, Name, Mod, Fmt, Opts, 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. -decode_error(true, {Rec, _, _}, Avp) -> - Avp#diameter_avp{value = Rec}; +%% Ignoring errors in Failed-AVP. +decode_error(true, Fmt, {Rec, ComponentAvps, _Errors}, Avp) -> + [set(Fmt, Avp, Rec) | ComponentAvps]; -decode_error(false, {_, ComponentAvps, [{RC,A} | _]}, Avp) -> +%% 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/6 %% %% Error when decoding a non-grouped AVP. @@ -553,15 +634,30 @@ decode_error(false, Reason, Name, Mod, Opts, Avp) -> ?MODULE, ?LINE, {Reason, Name, Avp#diameter_avp.name, Mod, Stack}), - rc(Reason, Avp, Opts, Mod). + 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_decode/5 +%% avp/6 -avp_decode(Data, AvpName, Opts, Mod, Mod) -> - Mod:avp(decode, Data, AvpName, Opts); +avp(T, Data, AvpName, Opts, Mod, Mod) -> + Mod:avp(T, Data, AvpName, Opts); -avp_decode(Data, AvpName, Opts, Mod, _) -> - Mod:avp(decode, Data, AvpName, Opts, Mod). +avp(T, Data, AvpName, Opts, _, Mod) -> + Mod:avp(T, Data, AvpName, Opts#{module := Mod}). %% set_strict/3 %% @@ -584,49 +680,57 @@ set_failed('Failed-AVP', #{failed_avp := false} = Opts) -> set_failed(_, Opts) -> Opts. -%% acc/9 +%% acc/8 -acc([AM | Acc], As, I, Name, Field, Arity, Strict, Mod, Opts) -> - [AM | acc1(Acc, As, I, Name, Field, Arity, Strict, Mod, Opts)]. +acc([AM | Acc], As, I, Field, Arity, Strict, Mod, Opts) -> + [AM | acc1(Acc, As, I, Field, Arity, Strict, Mod, Opts)]. -%% acc1/9 +%% acc1/8 %% Faulty AVP, not grouped. -acc1(Acc, {_RC, Avp} = E, _, _, _, _, _, _, _) -> +acc1(Acc, {_RC, Avp} = E, _, _, _, _, _, _) -> [Avps, Failed | Rec] = Acc, [[Avp | Avps], [E | Failed] | Rec]; %% Faulty component in grouped AVP. -acc1(Acc, {RC, As, Avp}, _, _, _, _, _, _, _) -> +acc1(Acc, {RC, As, Avp}, _, _, _, _, _, _) -> [Avps, Failed | Rec] = Acc, [[As | Avps], [{RC, Avp} | Failed] | Rec]; %% Grouped AVP ... -acc1([Avps | Acc], [Avp|_] = As, I, Name, Field, Arity, Strict, Mod, Opts) -> - [[As|Avps] | acc2(Acc, Avp, I, Name, Field, Arity, Strict, Mod, Opts)]; +acc1([Avps | Acc], [Avp|_] = As, I, Field, Arity, Strict, Mod, Opts) -> + [[As|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod, Opts)]; %% ... or not. -acc1([Avps | Acc], Avp, I, Name, Field, Arity, Strict, Mod, Opts) -> - [[Avp|Avps] | acc2(Acc, Avp, I, Name, Field, Arity, Strict, Mod, Opts)]. +acc1([Avps | Acc], Avp, I, Field, Arity, Strict, Mod, Opts) -> + [[Avp|Avps] | acc2(Acc, Avp, I, Field, Arity, Strict, Mod, Opts)]. + +%% 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/9 +%% acc2/8 %% No errors, but nowhere to pack. -acc2(Acc, Avp, _, _, 'AVP', 0, _, _, _) -> +acc2(Acc, Avp, _, 'AVP', 0, _, _, _) -> [Failed | Rec] = Acc, [[{rc(Avp), Avp} | Failed] | Rec]; %% Relaxed arities. -acc2(Acc, Avp, _, _, Field, Arity, Strict, Mod, _) +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, _) -> +acc2(Acc, Avp, _, Field, {_,'*'} = Arity, _, Mod, _) -> pack(Arity, Field, Avp, Mod, Acc); %% Or check. -acc2(Acc, Avp, I, _, Field, Arity, _, Mod, _) -> +acc2(Acc, Avp, I, Field, Arity, _, Mod, _) -> Mx = max_arity(Arity), if Mx =< I -> [Failed | Rec] = Acc, @@ -701,22 +805,6 @@ avp_arity(Name, 'AVP' = AvpName, Mod, Opts, M) -> avp_arity(Name, AvpName, Mod, _, _) -> Mod:avp_arity(Name, AvpName). -%% rc/4 - -%% Length error communicated from diameter_types or a -%% @custom_types/@codecs module. -rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = A, Opts, Mod) -> - {RC, A#diameter_avp{data = Mod:empty_value(AvpName, Opts)}}; - -%% 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}. - %% pack/5 pack(Arity, F, Avp, Mod, [Failed | Rec]) -> @@ -724,8 +812,9 @@ pack(Arity, F, Avp, Mod, [Failed | Rec]) -> %% set/5 -set(_, _, _, _, false = No) -> - No; +set(_, _, _, _, Name) + when is_atom(Name) -> + Name; set(1, F, Value, _, Map) when is_map(Map) -> @@ -819,8 +908,8 @@ empty(Name, #{module := Mod} = Opts) -> %% newrec/4 -newrec(false = No, _, _, _) -> - No; +newrec(none, _, Name, _) -> + Name; newrec(record, Mod, Name, T) when T /= decode -> diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl index 8792e97621..1c1ea42cb5 100644 --- a/lib/diameter/src/base/diameter_lib.erl +++ b/lib/diameter/src/base/diameter_lib.erl @@ -283,7 +283,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. %% --------------------------------------------------------------------------- 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 6c47d8804e..d99f11a697 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -238,7 +238,7 @@ 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), + Strict = proplists:get_value(strict_capx, Opts, true), LengthErr = proplists:get_value(length_errors, Opts, exit), {TPid, Addrs} = start_transport(T, Rest, Svc), @@ -252,7 +252,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) -> mode = M, service = svc(Svc, Addrs), length_errors = LengthErr, - strict = Strictness, + strict = Strict, incoming_maxlen = Maxlen, codec = maps:with([decode_format, string_decode, @@ -544,11 +544,11 @@ put_route(Pid) -> MRef = monitor(process, Pid), put(Pid, MRef). -%% get_route/2 +%% get_route/3 -%% incoming answer -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} -> @@ -559,8 +559,14 @@ get_route(_, #diameter_packet{header = #diameter_header{is_request = false}} false end; -%% incoming request -get_route(Ack, _) -> +%% Requests answered here ... +get_route(_, N, _) + when N == 'CER'; + N == 'DPR' -> + false; + +%% ... or not. +get_route(Ack, _, _) -> Ack. %% erase_route/1 @@ -745,7 +751,7 @@ recv1('DPA' = Name, %% 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, Pkt), Name, Pkt}, + Pid ! {recv, self(), get_route(Ack, Name, Pkt), Name, Pkt}, handle(Name, Pkt, S). %% pkt/2 diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 07f1fd3a4a..31dd92f878 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -115,8 +115,21 @@ 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 @@ -517,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. @@ -682,12 +702,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(lists:keydelete(monitor, 1, Opts))}, + monitor = mref(M), + options = maps:remove(monitor, SvcOpts)}, {S, Acc}; cfg_acc({_Ref, Type, _Opts} = T, {S, Acc}) @@ -702,8 +725,29 @@ init_peers() -> %% Alias, %% TPid} -service_options(Opts) -> - maps:from_list(lists:delete({strict_arities, true}, Opts)). +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; @@ -712,6 +756,8 @@ mref(P) -> 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, @@ -721,16 +767,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) -> []; @@ -809,7 +856,7 @@ start(Ref, Type, Opts, State) -> start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT, local = {PeerT, _, _}, options = #{string_decode := SD} - = SvcOpts0, + = SvcOpts, service_name = SvcName, service = Svc0}) when Type == connect; @@ -818,12 +865,12 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT, = Svc1 = merge_service(Opts, Svc0), Svc = binary_caps(Svc1, SD), - SvcOpts = merge_options(Opts, SvcOpts0), - RecvData = diameter_traffic:make_recvdata([SvcName, PeerT, Apps, SvcOpts]), - T = {Opts, SvcOpts, RecvData, Svc}, + {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] @@ -831,10 +878,14 @@ start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT, [], N). -merge_options(Opts, SvcOpts) -> - Keys = maps:keys(SvcOpts), - Map = maps:from_list([KV || {K,_} = KV <- Opts, lists:member(K, Keys)]), - maps:merge(SvcOpts, Map). +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; @@ -1403,9 +1454,15 @@ is_remote(Pid, T) -> %% # remote_peer_up/4 %% --------------------------------------------------------------------------- -remote_peer_up(TPid, Aliases, Caps, #state{options = #{use_shared_peers := T}} +remote_peer_up(TPid, Aliases, Caps, #state{options = #{use_shared_peers := T}, + remote = {PeerT, _, _}} = S) -> - is_remote(TPid, T) andalso rpu(TPid, Aliases, Caps, 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, @@ -1415,6 +1472,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, diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index bb8bd84faf..d2856ae530 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -78,6 +78,7 @@ sequence :: diameter:sequence(), counters :: boolean(), codec :: #{decode_format := diameter:decode_format(), + avp_dictionaries => nonempty_list(module()), string_decode := boolean(), strict_arities => diameter:strict_arities(), strict_mbit := boolean(), @@ -92,6 +93,7 @@ caller :: pid() | undefined, %% calling process handler :: pid(), %% request process peer :: undefined | {pid(), #diameter_caps{}}, + caps :: undefined, %% no longer used packet :: #diameter_packet{} | undefined}). %% of request %% --------------------------------------------------------------------------- @@ -107,6 +109,7 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> sequence = Mask, counters = B, codec = maps:with([decode_format, + avp_dictionaries, string_decode, strict_arities, strict_mbit, @@ -351,6 +354,8 @@ recv_request(Ack, No end. +%% decode/4 + decode(Id, Dict, #recvdata{codec = Opts}, Pkt) -> errors(Id, diameter_codec:decode(Id, Dict, Opts, Pkt)). @@ -1031,15 +1036,15 @@ answer_message(RC, origin_realm = {OR,_}}, #diameter_packet{avps = Avps, errors = Es}) -> - {Code, _, Vid} = Dict0:avp_header('Session-Id'), ['answer-message', {'Origin-Host', OH}, {'Origin-Realm', OR}, - {'Result-Code', RC}] - ++ session_id(Code, Vid, Avps) - ++ failed_avp(RC, Es). + {'Result-Code', RC} + | session_id(Dict0, Avps) + ++ failed_avp(RC, Es) + ++ proxy_info(Dict0, Avps)]. -session_id(Code, Vid, 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', [Bin]}] @@ -1057,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 ... @@ -1891,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 @@ -1909,12 +1918,12 @@ str(T) -> get_avp(?RELAY, Name, Msg) -> get_avp(?BASE, Name, Msg); -%% Message is a header/avps list. +%% Message as header/avps list. get_avp(Dict, Name, [#diameter_header{} | Avps]) -> try - {Code, _, VId} = Dict:avp_header(Name), - A = find_avp(Code, VId, Avps), - (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 @@ -1924,20 +1933,33 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) -> get_avp(_, Name, [_MsgName | Avps]) -> case find(Name, Avps) of {_, V} -> - #diameter_avp{name = Name, value = V}; + #diameter_avp{name = Name, value = value(Name, V)}; _ -> undefined end; -%% ... or 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) @@ -1967,14 +1989,25 @@ 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) when is_binary(Bin) -> V = Dict:avp(decode, Bin, Name, decode_opts(Dict)), - Avp#diameter_avp{value = V}; -avp_decode(_, _, #diameter_avp{} = Avp) -> - Avp. + 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). @@ -1991,4 +2024,5 @@ decode_opts(Dict) -> string_decode => false, strict_mbit => false, failed_avp => false, - dictionary => Dict}. + module => Dict, + app_dictionary => Dict}. diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index b2172356ee..43623334a9 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -72,14 +72,11 @@ restrict := boolean(), suspect := non_neg_integer(), %% OKAY -> SUSPECT okay := non_neg_integer()}, %% REOPEN -> OKAY - codec :: #{decode_format := false, + codec :: #{decode_format := none, string_decode := false, - strict_arities => diameter:strict_arities(), strict_mbit := boolean(), - failed_avp := false, rfc := 3588 | 6733, - ordered_encode := false, - incoming_maxlen := diameter:message_length()}, + ordered_encode := false}, shutdown = false :: boolean()}). %% --------------------------------------------------------------------------- @@ -137,15 +134,6 @@ i({Ack, T, Pid, {Opts, putr(restart, {T, Opts, Svc, SvcOpts}), %% save seeing it in trace putr(dwr, dwr(Caps)), %% Nodes = restrict_nodes(Restrict), - CodecKeys = [decode_format, - string_decode, - strict_arities, - strict_mbit, - incoming_maxlen, - spawn_opt, - rfc, - ordered_encode], - #watchdog{parent = Pid, transport = start(T, Opts, SvcOpts, Nodes, Dict0, Svc), tw = proplists:get_value(watchdog_timer, @@ -153,14 +141,21 @@ i({Ack, T, Pid, {Opts, ?DEFAULT_TW_INIT), receive_data = RecvData, dictionary = Dict0, - config = - maps:without([traffic_counters | CodecKeys], - config(SvcOpts#{restrict => restrict(Nodes), - suspect => 1, - okay => 3}, - Opts)), - codec = maps:with(CodecKeys -- [strict_arities], - SvcOpts#{decode_format := false, + 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})}. 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..143dede037 100644 --- a/lib/diameter/src/compiler/diameter_exprecs.erl +++ b/lib/diameter/src/compiler/diameter_exprecs.erl @@ -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/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/modules.mk b/lib/diameter/src/modules.mk index bb3b234d20..bb86de016a 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -24,6 +24,7 @@ DICTS = \ base_rfc6733 \ base_accounting \ acct_rfc6733 \ + doic_rfc7683 \ relay # The yecc grammar for the dictionary parser. diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 6a9f1f940b..4eb3379d59 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -79,7 +79,7 @@ -type option() :: {sender, boolean()} | sender | {packet, boolean() | raw} - | {message_cb, false | diameter:evaluable()}. + | {message_cb, false | diameter:eval()}. -type uint() :: non_neg_integer(). @@ -102,9 +102,10 @@ streams :: {uint(), uint()} %% {InStream, OutStream} counts | undefined, os = 0 :: uint(), %% next output stream + rotate = 1 :: boolean() | 0 | 1, %% rotate os? packet = true :: boolean() %% legacy transport_data? | raw, - message_cb = false :: false | diameter:evaluable(), + message_cb = false :: false | diameter:eval(), send = false :: pid() | boolean()}). %% sending process %% Monitor process state. @@ -112,7 +113,7 @@ {transport :: pid(), ack = false :: boolean(), socket :: gen_sctp:sctp_socket(), - assoc_id :: gen_sctp:assoc_id()}). %% next output stream + assoc_id :: gen_sctp:assoc_id()}). %% Listener process state. -record(listener, @@ -120,7 +121,7 @@ socket :: gen_sctp:sctp_socket(), service :: pid(), %% service process pending = {0, queue:new()}, - opts :: [[match()] | boolean() | diameter:evaluable()]}). + 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 @@ -156,12 +157,7 @@ start(T, Svc, Opts) = Svc, diameter_sctp_sup:start(), %% start supervisors on demand Addrs = Caps#diameter_caps.host_ip_address, - s(T, Addrs, Pid, 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 @@ -354,23 +350,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 -> @@ -379,6 +387,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) -> @@ -565,7 +581,7 @@ transition(Msg, S) %% Deferred actions from a message_cb. transition({actions, Dir, Acts}, S) -> - actions(Acts, Dir, S); + setopts(ok, actions(Acts, Dir, S)); %% Request to close the transport connection. transition({diameter, {close, Pid}}, #transport{parent = Pid}) -> @@ -677,11 +693,16 @@ send(#diameter_packet{transport_data = {outstream, SId}} = S) -> send(SId rem OS, Msg, S); -%% ... or not: rotate through all streams. -send(Msg, #transport{streams = {_, OS}, +%% ... or not: rotate when sending on multiple streams ... +send(Msg, #transport{rotate = true, + streams = {_, OS}, os = N} = S) -> - send(N, Msg, S#transport{os = (N + 1) rem OS}). + 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 @@ -749,7 +770,7 @@ recv({[#sctp_sndrcvinfo{assoc_id = Id}], _Bin} %% Inbound Diameter message. recv({[#sctp_sndrcvinfo{}], Bin} = Msg, S) when is_binary(Bin) -> - message(recv, Msg, S); + message(recv, Msg, recv(S)); recv({_, #sctp_shutdown_event{}}, _) -> stop; @@ -769,6 +790,25 @@ 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 = {_,N}, socket = Sock} = S) -> + ok = inet:setopts(Sock, [{sctp_default_send_param, + #sctp_sndrcvinfo{flags = [unordered]}}]), + S#transport{rotate = 1 < N}; + +recv(#transport{rotate = N} = S) -> + S#transport{rotate = N-1}. + +%% 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 diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index 6252dbddfb..a8639baa11 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -110,7 +110,7 @@ -type option() :: {port, non_neg_integer()} | {sender, boolean()} | sender - | {message_cb, false | diameter:evaluable()} + | {message_cb, false | diameter:eval()} | {fragment_timer, 0..16#FFFFFFFF}. %% Accepting/connecting transport process state. @@ -125,7 +125,7 @@ timeout :: infinity | 0..16#FFFFFFFF, %% fragment timeout tref = false :: false | reference(), %% fragment timer reference flush = false :: boolean(), %% flush fragment at timeout? - message_cb :: false | diameter:evaluable(), + message_cb :: false | diameter:eval(), send :: pid() | false}). %% sending process %% The usual transport using gen_tcp can be replaced by anything @@ -142,8 +142,7 @@ -> {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) -> @@ -258,22 +257,14 @@ i(#monitor{parent = Pid, transport = TPid} = S) -> 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, module = Mod}. -laddr([], Mod, Sock) -> - {ok, {Addr, _Port}} = sockname(Mod, Sock), - Addr; -laddr([{ip, Addr}], _, _) -> - Addr. - ssl_opts([]) -> false; ssl_opts([{ssl_options, true}]) -> @@ -308,24 +299,16 @@ init(accept = T, Ref, Mod, Pid, Opts, Addrs, SvcPid) -> Sock; init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SvcPid) -> - {[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]), - LAddrOpt = get_addr(LA, Addrs), + {[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]). @@ -382,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 @@ -413,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 @@ -640,7 +644,7 @@ transition(Msg, S) %% Deferred actions from a message_cb. transition({actions, Dir, Acts}, S) -> - actions(Acts, Dir, S); + setopts(actions(Acts, Dir, S)); %% Request to close the transport connection. transition({diameter, {close, Pid}}, #transport{parent = Pid, diff --git a/lib/diameter/test/diameter_codec_SUITE.erl b/lib/diameter/test/diameter_codec_SUITE.erl index c79b642c09..17112794e4 100644 --- a/lib/diameter/test/diameter_codec_SUITE.erl +++ b/lib/diameter/test/diameter_codec_SUITE.erl @@ -291,7 +291,7 @@ recode(Msg, Dict) -> recode(#diameter_packet{msg = Msg}, Dict). opts(Mod) -> - #{dictionary => Mod, + #{app_dictionary => Mod, decode_format => record, string_decode => false, strict_mbit => true, 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 735339ebb9..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 @@ -77,7 +77,7 @@ dec('BR', #diameter_packet ok. opts(Mod) -> - #{dictionary => Mod, + #{app_dictionary => Mod, decode_format => record, string_decode => true, strict_mbit => true, diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl index 22fb0550ea..70e910ffa6 100644 --- a/lib/diameter/test/diameter_codec_test.erl +++ b/lib/diameter/test/diameter_codec_test.erl @@ -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, @@ -216,7 +217,7 @@ avp(Mod, encode = X, V, Name, _) -> opts(Mod) -> (opts())#{module => Mod, - dictionary => Mod}. + app_dictionary => Mod}. opts() -> #{decode_format => record, diff --git a/lib/diameter/test/diameter_examples_SUITE.erl b/lib/diameter/test/diameter_examples_SUITE.erl index eb99f10fe6..ee44ed8dc9 100644 --- a/lib/diameter/test/diameter_examples_SUITE.erl +++ b/lib/diameter/test/diameter_examples_SUITE.erl @@ -344,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; @@ -352,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_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index d6d69eafa1..ffb4a508cd 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -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,8 +28,10 @@ -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, @@ -47,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, @@ -112,6 +116,8 @@ %% diameter_{tcp,sctp} callbacks -export([message/3]). +-include_lib("kernel/include/inet_sctp.hrl"). + -include("diameter.hrl"). -include("diameter_gen_base_rfc3588.hrl"). -include("diameter_gen_base_accounting.hrl"). @@ -127,12 +133,13 @@ %% 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, 42). +-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 records with the same names.) @@ -152,7 +159,7 @@ -define(ENCODINGS, [list, record, map]). %% How to decode incoming messages. --define(DECODINGS, [record, false, map, list, record_from_map]). +-define(DECODINGS, [record, none, map, list, record_from_map]). %% Which dictionary to use in the clients. -define(RFCS, [rfc3588, rfc6733, rfc4005]). @@ -211,6 +218,7 @@ {'Acct-Application-Id', [3]}, %% base accounting {restrict_connections, false}, {string_decode, Grp#group.strings}, + {avp_dictionaries, [diameter_gen_doic_rfc7683]}, {incoming_maxlen, 1 bsl 21} | [{application, [{dictionary, D}, {module, [?MODULE, Grp]}, @@ -273,12 +281,15 @@ all() -> -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() -> Names = names(), [{P, [P], Ts} || Ts <- [tc()], P <- [shuffle, parallel]] ++ - [{?util:name(N), [], [{group, if S -> shuffle; not S -> parallel end}]} - || [_,_,_,_,S|_] = N <- Names] + [{?util:name(N), [], [{group, if T == sctp; S -> shuffle; + true -> parallel end}]} + || [T,_,_,_,S|_] = N <- Names] ++ [{T, [], [{group, ?util:name(N)} || N <- names(Names, ?GROUPS), T == hd(N)]} @@ -306,6 +317,9 @@ names(_, Names) -> %% -------------------- +init_per_suite() -> + [{timetrap, {seconds, 60}}]. + init_per_suite(Config) -> [{rfc4005, compile_and_load()}, {sctp, ?util:have_sctp()} | Config]. @@ -316,6 +330,9 @@ end_per_suite(_Config) -> %% -------------------- +init_per_group(_) -> + [{timetrap, {seconds, 30}}]. + init_per_group(Name, Config) when Name == shuffle; Name == parallel -> @@ -352,7 +369,7 @@ init_per_group(Name, Config) -> server_decoding = D, server_sender = SS, server_throttle = ST}, - replace([{group, G}, {runlist, select()}], Config); + replace([{group, G}, {runlist, select(T)}], Config); _ -> Config end. @@ -366,9 +383,10 @@ end_per_group(Name, Config) end_per_group(_, _) -> ok. -select() -> - try rand:uniform(?LIMIT) of - N -> lists:sublist(?util:scramble(tc()), max(N,5)) +select(T) -> + try maps:get(T, ?LIMIT) of + N -> + lists:sublist(?util:scramble(tc()), max(5, rand:uniform(N))) catch error:_ -> ?LIMIT end. @@ -430,6 +448,7 @@ tc() -> send_protocol_error, send_experimental_result, send_arbitrary, + send_proxy_info, send_unknown, send_unknown_short, send_unknown_mandatory, @@ -495,6 +514,7 @@ start_services(Config) -> | ?SERVICE(SN, Grp)]), ok = diameter:start_service(CN, [{traffic_counters, bool()}, {sequence, ?CLIENT_MASK}, + {decode_format, map}, {strict_arities, decode} | ?SERVICE(CN, Grp)]). @@ -521,7 +541,7 @@ add_transports(Config) -> | server_apps()] ++ [{spawn_opt, {erlang, spawn, []}} || CS]), Cs = [?util:connect(CN, - [T, {sender, CS}], + [T, {sender, CS} | client_opts(T)], LRef, [{id, Id} | client_apps(R, [{'Origin-State-Id', origin(Id)}])]) @@ -531,6 +551,14 @@ add_transports(Config) -> Id <- [{D,E}]], ?util:write_priv(Config, "transport", [LRef | Cs]). +client_opts(tcp) -> + []; +client_opts(sctp) -> + [{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, @@ -612,7 +640,6 @@ result_codes(_Config) -> send_ok(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 1}], - ['ACA' | #{'Result-Code' := ?SUCCESS, 'Session-Id' := _}] = call(Config, Req). @@ -644,13 +671,80 @@ 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. @@ -672,6 +766,19 @@ send_arbitrary(Config) -> = call(Config, Req), "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, @@ -695,12 +802,12 @@ send_unknown_short(Config, M, RC) -> data = <<17>>}]}], ['ASA' | #{'Session-Id' := _, 'Result-Code' := RC, - 'Failed-AVP' := Avps}] + 'Failed-AVP' := [#{'AVP' := [Avp]}]}] = call(Config, Req), - [[#diameter_avp{code = 999, - is_mandatory = M, - data = <<17, _/binary>>}]] %% extra bits from padding - = failed_avps(Avps, Config). + #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) -> @@ -709,12 +816,12 @@ send_unknown_mandatory(Config) -> data = <<17>>}]}], ['ASA' | #{'Session-Id' := _, 'Result-Code' := ?AVP_UNSUPPORTED, - 'Failed-AVP' := Avps}] + 'Failed-AVP' := [#{'AVP' := [Avp]}]}] = call(Config, Req), - [[#diameter_avp{code = 999, - is_mandatory = true, - data = <<17>>}]] - = failed_avps(Avps, Config). + #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. @@ -729,13 +836,13 @@ send_unexpected_mandatory_decode(Config) -> data = <<12:32>>}]}], ['ASA' | #{'Session-Id' := _, 'Result-Code' := ?AVP_UNSUPPORTED, - 'Failed-AVP' := Avps}] + 'Failed-AVP' := [#{'AVP' := [Avp]}]}] = call(Config, Req), - [[#diameter_avp{code = 27, - is_mandatory = true, - value = 12, - data = <<12:32>>}]] - = failed_avps(Avps, Config). + #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) -> @@ -743,11 +850,11 @@ send_too_many(Config) -> ['ASA' | #{'Session-Id' := _, 'Result-Code' := ?TOO_MANY, - 'Failed-AVP' := Avps}] + 'Failed-AVP' := [#{'AVP' := [Avp]}]}] = call(Config, Req), - [[#diameter_avp{name = 'Auth-Application-Id', - value = 44}]] - = failed_avps(Avps, Config). + #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 @@ -759,12 +866,11 @@ send_grouped_error(Config) -> {'Proxy-State', ""}]]}], ['ASA' | #{'Session-Id' := _, 'Result-Code' := ?INVALID_AVP_LENGTH, - 'Failed-AVP' := Avps}] + 'Failed-AVP' := [#{'AVP' := [Avp]}]}] = call(Config, Req), - [[#diameter_avp{name = 'Proxy-Info', value = V}]] - = failed_avps(Avps, Config), - {Empty, undefined, []} = proxy_info(V, Config), - <<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) -> @@ -817,9 +923,8 @@ send_invalid_avp_length(Config) -> 'Result-Code' := ?INVALID_AVP_LENGTH, 'Origin-Host' := _, 'Origin-Realm' := _, - 'Failed-AVP' := Avps}] - = call(Config, Req), - [[_]] = failed_avps(Avps, Config). + 'Failed-AVP' := [#{'AVP' := [_]}]}] + = call(Config, Req). %% Send a request containing 5xxx errors that the server rejects with %% 3xxx. @@ -1030,29 +1135,6 @@ send_anything(Config) -> %% =========================================================================== -failed_avps(Avps, Config) -> - #group{client_dict = D} = proplists:get_value(group, Config), - [failed_avp(D, T) || T <- Avps]. - -failed_avp(nas4005, {'nas_Failed-AVP', As}) -> - As; -failed_avp(_, #'diameter_base_Failed-AVP'{'AVP' = As}) -> - As. - -proxy_info(Rec, Config) -> - #group{client_dict = D} = proplists:get_value(group, Config), - if D == nas4005 -> - {'nas_Proxy-Info', H, S, As} - = Rec, - {H,S,As}; - true -> - #'diameter_base_Proxy-Info'{'Proxy-Host' = H, - 'Proxy-State' = S, - 'AVP' = As} - = Rec, - {H,S,As} - end. - group(Config) -> #group{} = proplists:get_value(group, Config). @@ -1093,12 +1175,12 @@ origin(N) -> decode(record) -> 0; decode(list) -> 1; decode(map) -> 2; -decode(false) -> 3; +decode(none) -> 3; decode(record_from_map) -> 4; decode(0) -> record; decode(1) -> list; decode(2) -> map; -decode(3) -> false; +decode(3) -> none; decode(4) -> record_from_map. encode(record) -> 0; @@ -1145,16 +1227,18 @@ to_map(#diameter_packet{header = H, msg = Rec}, %% No record decode: do it ourselves. to_map(#diameter_packet{header = H, - msg = false, + msg = Name, bin = Bin}, - #group{server_decoding = false, + #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_packet{msg = [MsgName | _Map] = Msg} = diameter_codec:decode(dict(H), Opts, Bin), + {MsgName, _} = {Name, Msg}, %% assert Msg. dict(#diameter_header{application_id = Id, @@ -1522,24 +1606,23 @@ 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), - rec_to_map(answer(Ans, Es, Name), Dict). + 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; @@ -1716,11 +1799,33 @@ request(['ACR' | #{'Session-Id' := SId, 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}; +%% send_proxy_info +request(['ASR' | #{'Proxy-Info' := _}], + _) -> + {protocol_error, 3999}; + request(['ASR' | #{'Session-Id' := SId} = Avps], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> diff --git a/lib/diameter/test/diameter_transport_SUITE.erl b/lib/diameter/test/diameter_transport_SUITE.erl index 9d981d0a2b..284d2b9566 100644 --- a/lib/diameter/test/diameter_transport_SUITE.erl +++ b/lib/diameter/test/diameter_transport_SUITE.erl @@ -349,35 +349,40 @@ rand_bytes(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 03f79096ac..d249b0e4fa 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -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 |