aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/diameter/doc/src/diameter.xml28
-rw-r--r--lib/diameter/include/diameter.hrl4
-rw-r--r--lib/diameter/src/base/diameter.erl3
-rw-r--r--lib/diameter/src/base/diameter_config.erl11
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl205
5 files changed, 142 insertions, 109 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index ba9225da8b..accf21cf98 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -16,7 +16,7 @@
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -206,11 +206,13 @@ probably avoid it.</p>
<item>
<p>
Determines the manner in which incoming answer messages containing
-decode errors are handled.
+decode errors are handled.</p>
+
+<p>
If <c>callback</c> then errors result in a &app_handle_answer;
callback in the same fashion as for &app_handle_request;, with
errors communicated in the <c>errors</c> field of the
-<c>#diameter_packet{}</c> record passed to the callback.
+<c>#diameter_packet{}</c> passed to the callback.
If <c>report</c> then an answer containing errors is discarded
without a callback and a warning report is written to the log.
If <c>discard</c> then an answer containing errors is silently
@@ -224,6 +226,24 @@ question is as if a callback had taken place and returned
Defaults to <c>report</c> if unspecified.</p>
</item>
+<tag><c>{request_errors, answer_3xxx|callback}</c></tag>
+<item>
+<p>
+Determines the manner in which incoming requests are handled when an
+error other than 3007, DIAMETER_APPLICATION_UNSUPPORTED. (With which no
+application callback module can be associated.)</p>
+
+<p>
+If <c>answer_3xxx</c> then the request is answered by diameter
+without a &app_handle_request; callback taking place if a 3xxx series
+error (protocol errors) is detected.
+If <c>callback</c> then even 3xxx errors result in an application
+&app_handle_request; callback.</p>
+
+<p>
+Defaults to <c>answer_3xxx</c> if unspecified.</p>
+</item>
+
</taglist>
<marker id="call_opt"/>
@@ -534,7 +554,7 @@ Pkt = #diameter_packet{}
The RFC 3539 watchdog state machine has
transitioned into (<c>up</c>) or out of (<c>down</c>) the OKAY
state.
-If a <c>#diameter_packet{}</c> record is present in an <c>up</c> event
+If a <c>#diameter_packet{}</c> is present in an <c>up</c> event
then there has been a capabilties exchange on a newly established
transport connection and the record contains the received CER or CEA.
Otherwise a connection has reestablished without the loss or
diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl
index 5ee898c3dd..513665cec1 100644
--- a/lib/diameter/include/diameter.hrl
+++ b/lib/diameter/include/diameter.hrl
@@ -143,6 +143,6 @@
init_state, %% option 'state', initial callback state
id, %% 32-bit unsigned application identifier = Dict:id()
mutable = false, %% boolean(), do traffic callbacks modify state?
- options = [{answer_errors, report}]}). %% | callback | discard
-
+ options = [{answer_errors, report}, %% | callback | discard
+ {request_errors, answer_3xxx}]}). %% | callback
-endif. %% -ifdef(diameter_hrl).
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index f563d244f6..7359688404 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -306,7 +306,8 @@ call(SvcName, App, Message) ->
| {module, app_module()}
| {state, any()}
| {call_mutates_state, boolean()}
- | {answer_errors, callback|report|discard}.
+ | {answer_errors, callback|report|discard}
+ | {request_errors, callback|answer_3xxx}.
-type app_alias()
:: any().
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 1486071573..889c75e3da 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -672,13 +672,15 @@ app_acc({application, Opts}, Acc) ->
ModS = get_opt(state, Opts, Alias),
M = get_opt(call_mutates_state, Opts, false),
A = get_opt(answer_errors, Opts, report),
+ R = get_opt(request_errors, Opts, answer_3xxx),
[#diameter_app{alias = Alias,
dictionary = Dict,
id = cb(Dict, id),
module = init_mod(Mod),
init_state = ModS,
mutable = init_mutable(M),
- options = [{answer_errors, init_answers(A)}]}
+ options = [{answer_errors, init_answers(A)},
+ {request_errors, init_request_errors(R)}]}
| Acc];
app_acc(_, Acc) ->
Acc.
@@ -722,6 +724,13 @@ init_answers(A)
init_answers(A) ->
?THROW({answer_errors, A}).
+init_request_errors(P)
+ when callback == P;
+ answer_3xxx == P ->
+ P;
+init_request_errors(P) ->
+ ?THROW({request_errors, P}).
+
%% Get a single value at the specified key.
get_opt(Keys, List)
when is_list(Keys) ->
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 0de3825943..c3b9c195fb 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -205,16 +205,25 @@ recv_request({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
Caps,
Dict0,
RecvData,
- diameter_codec:decode(Id, Dict, Pkt));
+ errors(Id, diameter_codec:decode(Id, Dict, Pkt)));
%% Note that the decode is different depending on whether or not Id is
%% ?APP_ID_RELAY.
%% DIAMETER_APPLICATION_UNSUPPORTED 3007
%% A request was sent for an application that is not supported.
-recv_request(#diameter_caps{} = Caps, TPid, Pkt, Dict0, _) ->
- As = collect_avps(Pkt),
- protocol_error(3007, TPid, Caps, Dict0, Pkt#diameter_packet{avps = As});
+recv_request(#diameter_caps{}
+ = Caps,
+ TPid,
+ #diameter_packet{errors = Es}
+ = Pkt,
+ Dict0,
+ _) ->
+ protocol_error(TPid,
+ Caps,
+ Dict0,
+ Pkt#diameter_packet{avps = collect_avps(Pkt),
+ errors = [3007 | Es]});
recv_request(false, _, _, _, _) -> %% transport has gone down
ok.
@@ -229,75 +238,25 @@ collect_avps(Pkt) ->
%% recv_R/6
-%% Wrong number of bits somewhere in the message: reply.
-%%
-%% DIAMETER_INVALID_AVP_BITS 3009
-%% A request was received that included an AVP whose flag bits are
-%% set to an unrecognized value, or that is inconsistent with the
-%% AVP's definition.
-%%
-recv_R(_App,
+%% Answer 3xxx errors ourselves ...
+recv_R(#diameter_app{options = [_, {request_errors, answer_3xxx} | _]},
TPid,
Caps,
Dict0,
_RecvData,
- #diameter_packet{errors = [Bs | _]} = Pkt)
- when is_bitstring(Bs) ->
- protocol_error(3009, TPid, Caps, Dict0, Pkt);
+ #diameter_packet{errors = [RC|_]} = Pkt)
+ when 3 == RC div 1000 ->
+ protocol_error(TPid, Caps, Dict0, Pkt);
-%% Either we support this application but don't recognize the command
-%% or we're a relay and the command isn't proxiable.
-%%
-%% DIAMETER_COMMAND_UNSUPPORTED 3001
-%% The Request contained a Command-Code that the receiver did not
-%% recognize or support. This MUST be used when a Diameter node
-%% receives an experimental command that it does not understand.
-%%
-recv_R(#diameter_app{id = Id},
+%% ... or make a handle_request callback. Note that
+%% Pkt#diameter_packet.msg = undefined in the 3001 case.
+recv_R(App,
TPid,
Caps,
Dict0,
- _RecvData,
- #diameter_packet{header = #diameter_header{is_proxiable = P},
- msg = M}
- = Pkt)
- when ?APP_ID_RELAY /= Id, undefined == M;
- ?APP_ID_RELAY == Id, not P ->
- protocol_error(3001, TPid, Caps, Dict0, Pkt);
-
-%% Error bit was set on a request.
-%%
-%% DIAMETER_INVALID_HDR_BITS 3008
-%% A request was received whose bits in the Diameter header were
-%% either set to an invalid combination, or to a value that is
-%% inconsistent with the command code's definition.
-%%
-recv_R(_App,
- TPid,
- Caps,
- Dict0,
- _RecvData,
- #diameter_packet{header = #diameter_header{is_error = true}}
- = Pkt) ->
- protocol_error(3008, TPid, Caps, Dict0, Pkt);
-
-%% A message in a locally supported application or a proxiable message
-%% in the relay application. Don't distinguish between the two since
-%% each application has its own callback config. That is, the user can
-%% easily distinguish between the two cases.
-recv_R(App, TPid, Caps, Dict0, RecvData, Pkt) ->
- request_cb(App, TPid, Caps, Dict0, RecvData, examine(Pkt)).
-
-%% Note that there may still be errors but these aren't protocol
-%% (3xxx) errors that lead to an answer-message.
-
-request_cb(App,
- TPid,
- Caps,
- Dict0,
- #recvdata{service_name = SvcName}
- = RecvData,
- Pkt) ->
+ #recvdata{service_name = SvcName}
+ = RecvData,
+ Pkt) ->
request_cb(cb(App, handle_request, [Pkt, SvcName, {TPid, Caps}]),
App,
TPid,
@@ -307,20 +266,21 @@ request_cb(App,
[],
Pkt).
-%% examine/1
+%% errors/1
%%
-%% Look for errors in a decoded message. It's odd/unfortunate that
-%% 501[15] aren't protocol errors.
+%% Look for errors in a decoded message, prepending the errors field
+%% with the first detected error. It's odd/unfortunate that 5011 isn't
+%% a protocol error.
%% DIAMETER_INVALID_MESSAGE_LENGTH 5015
%%
%% This error is returned when a request is received with an invalid
%% message length.
-examine(#diameter_packet{header = #diameter_header{length = Len},
- bin = Bin,
- errors = Es}
- = Pkt)
+errors(_, #diameter_packet{header = #diameter_header{length = Len},
+ bin = Bin,
+ errors = Es}
+ = Pkt)
when Len < 20;
0 /= Len rem 4;
8*Len /= bit_size(Bin) ->
@@ -330,13 +290,47 @@ examine(#diameter_packet{header = #diameter_header{length = Len},
%% This error is returned when a request was received, whose version
%% number is unsupported.
-examine(#diameter_packet{header = #diameter_header{version = V},
- errors = Es}
- = Pkt)
+errors(_, #diameter_packet{header = #diameter_header{version = V},
+ errors = Es}
+ = Pkt)
when V /= ?DIAMETER_VERSION ->
Pkt#diameter_packet{errors = [5011 | Es]};
-examine(Pkt) ->
+%% DIAMETER_INVALID_AVP_BITS 3009
+%% A request was received that included an AVP whose flag bits are
+%% set to an unrecognized value, or that is inconsistent with the
+%% AVP's definition.
+
+errors(_, #diameter_packet{errors = [Bs | Es]} = Pkt)
+ when is_bitstring(Bs) ->
+ Pkt#diameter_packet{errors = [3009 | Es]};
+
+%% DIAMETER_COMMAND_UNSUPPORTED 3001
+%% The Request contained a Command-Code that the receiver did not
+%% recognize or support. This MUST be used when a Diameter node
+%% receives an experimental command that it does not understand.
+
+errors(Id, #diameter_packet{header = #diameter_header{is_proxiable = P},
+ msg = M,
+ errors = Es}
+ = Pkt)
+ when ?APP_ID_RELAY /= Id, undefined == M; %% don't know the command
+ ?APP_ID_RELAY == Id, not P -> %% command isn't proxiable
+ Pkt#diameter_packet{errors = [3001 | Es]};
+
+%% DIAMETER_INVALID_HDR_BITS 3008
+%% A request was received whose bits in the Diameter header were
+%% either set to an invalid combination, or to a value that is
+%% inconsistent with the command code's definition.
+
+errors(_, #diameter_packet{header = #diameter_header{is_request = true,
+ is_error = true},
+ errors = Es}
+ = Pkt) ->
+ Pkt#diameter_packet{errors = [3008 | Es]};
+
+%% Green.
+errors(_, Pkt) ->
Pkt.
%% request_cb/8
@@ -365,7 +359,7 @@ request_cb({protocol_error, RC},
_RecvData,
Fs,
Pkt)
- when 3000 =< RC, RC < 4000 ->
+ when 3 == RC div 1000 ->
protocol_error(RC, TPid, Caps, Dict0, Fs, Pkt);
%% RFC 3588 says we must reply 3001 to anything unrecognized or
@@ -447,28 +441,36 @@ dict(Dict, Dict0, Rec) ->
error:_ -> Dict
end.
-%% protocol_error/6
-
-protocol_error(RC, TPid, Caps, Dict0, Fs, Pkt) ->
- #diameter_caps{origin_host = {OH,_},
- origin_realm = {OR,_}}
- = Caps,
- #diameter_packet{avps = Avps, errors = Es}
- = Pkt,
+%% protocol_error/5
+protocol_error(TPid,
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}},
+ Dict0,
+ Fs,
+ #diameter_packet{avps = Avps,
+ errors = [RC|_]}
+ = Pkt) ->
?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Dict0, Avps),
- Dict0,
- TPid,
- Fs,
- Pkt#diameter_packet{errors = [RC | Es]}).
+ reply(answer_message({OH, OR, RC}, Dict0, Avps), Dict0, TPid, Fs, Pkt).
%% Note that reply/5 may set the result code once more. It's set in
%% answer_message/3 in case reply/5 doesn't.
-%% protocol_error/5
+protocol_error(TPid, Caps, Dict0, Pkt) ->
+ protocol_error(TPid, Caps, Dict0, [], Pkt).
-protocol_error(RC, TPid, Caps, Dict0, Pkt) ->
- protocol_error(RC, TPid, Caps, Dict0, [], Pkt).
+protocol_error(RC,
+ TPid,
+ Caps,
+ Dict0,
+ Fs,
+ #diameter_packet{errors = Es}
+ = Pkt) ->
+ protocol_error(TPid,
+ Caps,
+ Dict0,
+ Fs,
+ Pkt#diameter_packet{errors = [RC | Es]}).
%% resend/7
%%
@@ -661,15 +663,15 @@ rc(RC) ->
%% rc/4
-rc(#diameter_packet{msg = Rec} = Pkt, RC, Failed, DictT) ->
- Pkt#diameter_packet{msg = rc(Rec, RC, Failed, DictT)};
+rc(#diameter_packet{msg = Rec} = Pkt, RC, Failed, Dict) ->
+ Pkt#diameter_packet{msg = rc(Rec, RC, Failed, Dict)};
-rc(Rec, RC, Failed, DictT)
+rc(Rec, RC, Failed, Dict)
when is_integer(RC) ->
set(Rec,
- lists:append([rc(Rec, {'Result-Code', RC}, DictT),
- failed_avp(Rec, Failed, DictT)]),
- DictT).
+ lists:append([rc(Rec, {'Result-Code', RC}, Dict),
+ failed_avp(Rec, Failed, Dict)]),
+ Dict).
%% Reply as name and tuple list ...
set([_|_] = Ans, Avps, _) ->
@@ -1268,11 +1270,12 @@ handle_answer(SvcName, App, {error, Req, Reason}) ->
handle_error(App, Req, Reason, SvcName);
handle_answer(SvcName,
- #diameter_app{dictionary = Dict}
+ #diameter_app{dictionary = Dict,
+ id = Id}
= App,
{answer, Req, Dict0, Pkt}) ->
Mod = dict(Dict, Dict0, Pkt),
- answer(examine(diameter_codec:decode(Mod, Pkt)),
+ answer(errors(Id, diameter_codec:decode(Mod, Pkt)),
SvcName,
Mod,
App,