aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/src')
-rw-r--r--lib/diameter/src/base/diameter_codec.erl30
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl37
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl172
-rw-r--r--lib/diameter/src/base/diameter_watchdog.erl30
4 files changed, 148 insertions, 121 deletions
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 52eb10b8c2..a4d816db4e 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -21,6 +21,7 @@
-module(diameter_codec).
-export([encode/2,
+ encode/3,
decode/3,
decode/4,
collect_avps/1,
@@ -67,13 +68,22 @@
%%% # encode/2
%%% ---------------------------------------------------------------------------
--spec encode(module(), Msg :: term())
+encode(Mod, Msg) ->
+ encode(Mod, #{ordered_encode => true}, Msg).
+
+%%% ---------------------------------------------------------------------------
+%%% # encode/3
+%%% ---------------------------------------------------------------------------
+
+-spec encode(module(),
+ map(),
+ Msg :: term())
-> #diameter_packet{}
| no_return().
-encode(Mod, #diameter_packet{} = Pkt) ->
+encode(Mod, Opts, #diameter_packet{} = Pkt) ->
try
- encode(Mod, _Opts = [], Pkt)
+ enc(Mod, Opts, Pkt)
catch
exit: {Reason, Stack, #diameter_header{} = H} = T ->
%% Exit with a header in the reason to let the caller
@@ -86,18 +96,18 @@ encode(Mod, #diameter_packet{} = Pkt) ->
exit({?MODULE, encode, T})
end;
-encode(Mod, Msg) ->
+encode(Mod, Opts, Msg) ->
Seq = diameter_session:sequence(),
Hdr = #diameter_header{version = ?DIAMETER_VERSION,
end_to_end_id = Seq,
hop_by_hop_id = Seq},
- encode(Mod, #diameter_packet{header = Hdr,
- msg = Msg}).
+ encode(Mod, Opts, #diameter_packet{header = Hdr,
+ msg = Msg}).
-%% encode/3
+%% enc/3
-encode(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]}
- = Pkt) ->
+enc(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]}
+ = Pkt) ->
try encode_avps(reorder(As), Opts) of
Avps ->
Bin = list_to_binary(Avps),
@@ -126,7 +136,7 @@ encode(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]}
exit({Reason, diameter_lib:get_stacktrace(), Hdr})
end;
-encode(Mod, Opts, #diameter_packet{header = Hdr0, msg = Msg} = Pkt) ->
+enc(Mod, Opts, #diameter_packet{header = Hdr0, msg = Msg} = Pkt) ->
MsgName = rec2msg(Mod, Msg),
{Code, Flags, Aid} = msg_header(Mod, MsgName, Hdr0),
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index e0bcc565e7..a5dab45684 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -130,7 +130,8 @@
%% diameter:call/4.
codec :: #{string_decode := boolean(),
strict_mbit := boolean(),
- rfc := 3588 | 6733},
+ rfc := 3588 | 6733,
+ ordered_encode := false},
strict :: boolean(),
ack = false :: boolean(),
length_errors :: exit | handle | discard,
@@ -252,7 +253,11 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) ->
length_errors = LengthErr,
strict = Strictness,
incoming_maxlen = Maxlen,
- codec = maps:with([string_decode, strict_mbit, rfc], SvcOpts)}.
+ codec = maps:with([string_decode,
+ strict_mbit,
+ rfc,
+ ordered_encode],
+ SvcOpts#{ordered_encode => false})}.
%% The transport returns its local ip addresses so that different
%% transports on the same service can use different local addresses.
%% The local addresses are put into Host-IP-Address avps here when
@@ -592,7 +597,8 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
mode = {connect, Remote},
service = #diameter_service{capabilities = LCaps},
transport = TPid,
- dictionary = Dict}
+ dictionary = Dict,
+ codec = Opts}
= S) ->
OH = LCaps#diameter_caps.origin_host,
req_send_CER(OH, Remote)
@@ -602,7 +608,7 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
#diameter_packet{header = #diameter_header{end_to_end_id = Eid,
hop_by_hop_id = Hid}}
= Pkt
- = encode(CER, Dict),
+ = encode(CER, Opts, Dict),
incr(send, Pkt, Dict),
send(TPid, Pkt),
?LOG(send, 'CER'),
@@ -631,15 +637,15 @@ build_CER(#state{service = #diameter_service{capabilities = LCaps},
{ok, CER} = diameter_capx:build_CER(LCaps, Dict),
CER.
-%% encode/2
+%% encode/3
-encode(Rec, Dict) ->
+encode(Rec, Opts, Dict) ->
Seq = diameter_session:sequence({_,_} = getr(?SEQUENCE_KEY)),
Hdr = #diameter_header{version = ?DIAMETER_VERSION,
end_to_end_id = Seq,
hop_by_hop_id = Seq},
- diameter_codec:encode(Dict, #diameter_packet{header = Hdr,
- msg = Rec}).
+ diameter_codec:encode(Dict, Opts, #diameter_packet{header = Hdr,
+ msg = Rec}).
%% incoming/2
@@ -915,7 +921,10 @@ handle_request(Name,
%% send_answer/3
-send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) ->
+send_answer(Type, ReqPkt, #state{transport = TPid,
+ dictionary = Dict,
+ codec = Opts}
+ = S) ->
incr_error(recv, ReqPkt, Dict),
#diameter_packet{header = H,
@@ -934,7 +943,7 @@ send_answer(Type, ReqPkt, #state{transport = TPid, dictionary = Dict} = S) ->
msg = Msg,
transport_data = TD},
- AnsPkt = diameter_codec:encode(Dict, Pkt),
+ AnsPkt = diameter_codec:encode(Dict, Opts, Pkt),
incr(send, AnsPkt, Dict),
incr_rc(send, AnsPkt, Dict),
@@ -1358,8 +1367,9 @@ dpr([], [Reason | _], S) ->
-record(opts, {cause, timeout}).
-send_dpr(Reason, Opts, #state{dictionary = Dict,
- service = #diameter_service{capabilities = Caps}}
+send_dpr(Reason, DprOpts, #state{dictionary = Dict,
+ service = #diameter_service{capabilities = Caps},
+ codec = Opts}
= S) ->
#opts{cause = Cause, timeout = Tmo}
= lists:foldl(fun opt/2,
@@ -1368,7 +1378,7 @@ send_dpr(Reason, Opts, #state{dictionary = Dict,
_ -> ?REBOOT
end,
timeout = dpa_timeout()},
- Opts),
+ DprOpts),
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}
= Caps,
@@ -1376,6 +1386,7 @@ send_dpr(Reason, Opts, #state{dictionary = Dict,
Pkt = encode(['DPR', {'Origin-Host', OH},
{'Origin-Realm', OR},
{'Disconnect-Cause', Cause}],
+ Opts,
Dict),
send_dpr(false, Pkt, Tmo, S).
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index cbccf5d006..daba440586 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -103,6 +103,7 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) ->
sequence = Mask,
codec = maps:with([string_decode,
strict_mbit,
+ ordered_encode,
incoming_maxlen],
SvcOpts)}}.
@@ -479,11 +480,12 @@ request_cb(T, App, _, _) ->
%% send_A/4
-send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application
+send_A({Caps, Pkt}, TPid, Dict0, RecvData) -> %% unsupported application
#diameter_packet{errors = [RC|_]} = Pkt,
send_A(answer_message(RC, Caps, Dict0, Pkt),
TPid,
{Dict0, Dict0},
+ RecvData,
Pkt,
[],
[]);
@@ -492,6 +494,7 @@ send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) ->
send_A(answer(T, Caps, Pkt, App, Dict0, RecvData),
TPid,
{App#diameter_app.dictionary, Dict0},
+ RecvData,
Pkt,
EvalPktFs,
EvalFs);
@@ -499,10 +502,17 @@ send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) ->
send_A(_, _, _, _) ->
ok.
-%% send_A/6
+%% send_A/7
-send_A(T, TPid, {AppDict, Dict0} = DictT0, ReqPkt, EvalPktFs, EvalFs) ->
- {MsgDict, Pkt} = reply(T, TPid, DictT0, EvalPktFs, ReqPkt),
+send_A(T,
+ TPid,
+ {AppDict, Dict0}
+ = DictT0,
+ RecvData,
+ ReqPkt,
+ EvalPktFs,
+ EvalFs) ->
+ {MsgDict, Pkt} = reply(T, TPid, DictT0, RecvData, EvalPktFs, ReqPkt),
incr(send, Pkt, TPid, AppDict),
incr_rc(send, Pkt, TPid, {MsgDict, AppDict, Dict0}), %% count outgoing
send(TPid, Pkt, _Route = self()),
@@ -670,32 +680,43 @@ is_loop(Code, Vid, OH, Dict0, [_ | Avps])
is_loop(Code, Vid, OH, Dict0, Avps) ->
is_loop(Code, Vid, list_to_binary(OH), Dict0, Avps).
-%% reply/5
+%% reply/6
%% Local answer ...
-reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
- local(Ans, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt);
+reply({MsgDict, Ans}, TPid, {AppDict, Dict0}, RecvData, Fs, ReqPkt) ->
+ local(Ans, TPid, {MsgDict, AppDict, Dict0}, RecvData, Fs, ReqPkt);
%% ... or relayed.
-reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, Fs, _ReqPkt) ->
+reply(#diameter_packet{} = Pkt, _TPid, {AppDict, Dict0}, _, Fs, _ReqPkt) ->
eval_packet(Pkt, Fs),
{msg_dict(AppDict, Dict0, Pkt), Pkt}.
-%% local/5
+%% local/6
%%
%% Send a locally originating reply.
%% Skip the setting of Result-Code and Failed-AVP's below. This is
%% undocumented and shouldn't be relied on.
-local([Msg], TPid, DictT, Fs, ReqPkt)
+local([Msg], TPid, DictT, RecvData, Fs, ReqPkt)
when is_list(Msg);
is_tuple(Msg) ->
- local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []});
-
-local(Msg, TPid, {MsgDict, AppDict, Dict0}, Fs, ReqPkt) ->
+ local(Msg,
+ TPid,
+ DictT,
+ RecvData,
+ Fs,
+ ReqPkt#diameter_packet{errors = []});
+
+local(Msg,
+ TPid,
+ {MsgDict, AppDict, Dict0},
+ #recvdata{codec = Opts},
+ Fs,
+ ReqPkt) ->
Pkt = encode({MsgDict, AppDict},
TPid,
make_answer_packet(Msg, ReqPkt, MsgDict, Dict0),
+ Opts,
Fs),
{MsgDict, Pkt}.
@@ -1344,7 +1365,11 @@ send_request({{TPid, _Caps} = TC, App}
case prepare(cb(App, prepare_request, [Pkt, SvcName, TC]), []) of
[Msg | Fs] ->
ReqPkt = make_request_packet(Msg, Pkt),
- EncPkt = encode(App#diameter_app.dictionary, TPid, ReqPkt, Fs),
+ EncPkt = encode(App#diameter_app.dictionary,
+ TPid,
+ ReqPkt,
+ SvcOpts,
+ Fs),
T = send_R(ReqPkt, EncPkt, Transport, CallOpts, Caller, SvcName),
Ans = recv_answer(SvcName, App, CallOpts, T),
handle_answer(SvcName, SvcOpts, App, Ans);
@@ -1504,7 +1529,8 @@ send_R(ReqPkt,
%% recv_answer/4
-recv_answer(SvcName, App, CallOpts, {TRef, MRef, #request{ref = Ref} = Req}) ->
+recv_answer(SvcName, App, CallOpts, {TRef, MRef, #request{ref = Ref}
+ = Req}) ->
%% Matching on TRef below ensures we ignore messages that pertain
%% to a previous transport prior to failover. The answer message
%% includes the pid of the transport on which it was received,
@@ -1611,11 +1637,32 @@ handle_error(Hdr, SvcName, discard) ->
%% resend_request/4
-resend_request({{_, App} = Transport, _}, Req, CallOpts, SvcName) ->
- try retransmit(Transport, Req, SvcName, CallOpts#options.timeout) of
- T -> recv_answer(SvcName, App, CallOpts, T)
- catch
- ?FAILURE(Reason) -> {error, Req, Reason}
+resend_request({{{TPid, _Caps} = TC, App}, SvcOpts},
+ Req0,
+ #options{timeout = Timeout}
+ = CallOpts,
+ SvcName) ->
+ case
+ undefined == get(TPid)
+ andalso prepare_retransmit(TC, App, Req0, SvcName)
+ of
+ [ReqPkt | Fs] ->
+ AppDict = App#diameter_app.dictionary,
+ EncPkt = encode(AppDict, TPid, ReqPkt, SvcOpts, Fs),
+ Req = Req0#request{peer = TC,
+ packet = ReqPkt},
+ ?LOG(retransmission, EncPkt#diameter_packet.header),
+ incr(TPid, {msg_id(EncPkt, AppDict), send, retransmission}),
+ {TRef, MRef} = zend_requezt(TPid, EncPkt, Req, SvcName, Timeout),
+ recv_answer(SvcName, App, CallOpts, {TRef, MRef, Req});
+ false ->
+ {error, Req0, timeout};
+ {discard, Reason} ->
+ {error, Req0, Reason};
+ discard ->
+ {error, Req0, discarded};
+ {error, T} ->
+ ?ERROR({invalid_return, T, prepare_retransmit, App})
end;
resend_request(_, Req, _, _) -> %% no alternate peer
@@ -1651,29 +1698,29 @@ msg(#diameter_packet{msg = undefined, bin = Bin}) ->
msg(#diameter_packet{msg = Msg}) ->
Msg.
-%% encode/4
+%% encode/5
-encode(Dict, TPid, Pkt, Fs) ->
- P = encode(Dict, TPid, Pkt),
+encode(Dict, TPid, Pkt, Opts, Fs) ->
+ P = encode(Dict, TPid, Opts, Pkt),
eval_packet(P, Fs),
P.
-%% encode/2
+%% encode/4
%% Note that prepare_request can return a diameter_packet containing a
%% header or transport_data. Even allow the returned record to contain
%% an encoded binary. This isn't the usual case and doesn't properly
%% support retransmission but is useful for test.
-encode(Dict, TPid, Pkt)
+encode(Dict, TPid, Opts, Pkt)
when is_atom(Dict) ->
- encode({Dict, Dict}, TPid, Pkt);
+ encode({Dict, Dict}, TPid, Opts, Pkt);
%% A message to be encoded.
-encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) ->
+encode(DictT, TPid, Opts, #diameter_packet{bin = undefined} = Pkt) ->
{Dict, AppDict} = DictT,
try
- diameter_codec:encode(Dict, Pkt)
+ diameter_codec:encode(Dict, Opts, Pkt)
catch
exit: {diameter_codec, encode, T} = Reason ->
incr_error(send, T, TPid, AppDict),
@@ -1681,7 +1728,7 @@ encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) ->
end;
%% An encoded binary: just send.
-encode(_, _, #diameter_packet{} = Pkt) ->
+encode(_, _, _, #diameter_packet{} = Pkt) ->
Pkt.
%% zend_requezt/5
@@ -1752,70 +1799,21 @@ recv(TPid, Pid, TRef, {LocalTRef, MRef}) ->
send(Pid, Pkt, Route) ->
Pid ! {send, Pkt, Route}.
-%% retransmit/4
+%% prepare_retransmit/4
-retransmit({{TPid, _Caps} = TC, App}
- = Transport,
- #request{packet = ReqPkt}
- = Req,
- SvcName,
- Timeout) ->
- undefined == get(TPid) %% Don't failover to a peer we've
- orelse ?THROW(timeout), %% already sent to.
+prepare_retransmit({_TPid, _Caps} = TC, App, Req, SvcName) ->
+ Pkt = make_retransmit_packet(Req#request.packet),
- Pkt = make_retransmit_packet(ReqPkt),
+ case prepare(cb(App, prepare_retransmit, [Pkt, SvcName, TC]), []) of
+ [Msg | Fs] ->
+ [make_request_packet(Msg, Pkt) | Fs];
+ No ->
+ No
+ end.
- retransmit(cb(App, prepare_retransmit, [Pkt, SvcName, TC]),
- Transport,
- Req#request{packet = Pkt},
- SvcName,
- Timeout,
- []).
%% When sending a binary, it's up to prepare_retransmit to modify it
%% accordingly.
-retransmit({send, Msg},
- Transport,
- #request{packet = Pkt}
- = Req,
- SvcName,
- Timeout,
- Fs) ->
- resend_request(make_request_packet(Msg, Pkt),
- Transport,
- Req,
- SvcName,
- Timeout,
- Fs);
-
-retransmit({discard, Reason}, _, _, _, _, _) ->
- ?THROW(Reason);
-
-retransmit(discard, _, _, _, _, _) ->
- ?THROW(discarded);
-
-retransmit({eval_packet, RC, F}, Transport, Req, SvcName, Timeout, Fs) ->
- retransmit(RC, Transport, Req, SvcName, Timeout, [F|Fs]);
-
-retransmit(T, {_, App}, _, _, _, _) ->
- ?ERROR({invalid_return, T, prepare_retransmit, App}).
-
-resend_request(ReqPkt,
- {{TPid, _Caps} = TC, #diameter_app{dictionary = AppDict}},
- Req0,
- SvcName,
- Tmo,
- Fs) ->
- EncPkt = encode(AppDict, TPid, ReqPkt, Fs),
-
- Req = Req0#request{peer = TC,
- packet = ReqPkt},
-
- ?LOG(retransmission, EncPkt#diameter_packet.header),
- incr(TPid, {msg_id(EncPkt, AppDict), send, retransmission}),
- {TRef, MRef} = zend_requezt(TPid, EncPkt, Req, SvcName, Tmo),
- {TRef, MRef, Req}.
-
%% peer_monitor/2
peer_monitor(TPid, TRef) ->
diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl
index b827925400..a63425d92a 100644
--- a/lib/diameter/src/base/diameter_watchdog.erl
+++ b/lib/diameter/src/base/diameter_watchdog.erl
@@ -76,6 +76,7 @@
strict_mbit := boolean(),
failed_avp := false,
rfc := 3588 | 6733,
+ ordered_encode := false,
incoming_maxlen := diameter:message_length()},
shutdown = false :: boolean()}).
@@ -134,7 +135,12 @@ 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 = [string_decode, strict_mbit, incoming_maxlen, spawn_opt, rfc],
+ CodecKeys = [string_decode,
+ strict_mbit,
+ incoming_maxlen,
+ spawn_opt,
+ rfc,
+ ordered_encode],
#watchdog{parent = Pid,
transport = start(T, Opts, SvcOpts, Nodes, Dict0, Svc),
@@ -149,7 +155,8 @@ i({Ack, T, Pid, {Opts,
suspect => 1,
okay => 3},
Opts)),
- codec = maps:with(CodecKeys, SvcOpts#{string_decode := false})}.
+ codec = maps:with(CodecKeys, SvcOpts#{string_decode := false,
+ ordered_encode => false})}.
wait(Ref, Pid) ->
receive
@@ -502,9 +509,9 @@ getr(Key) ->
eraser(Key) ->
erase({?MODULE, Key}).
-%% encode/3
+%% encode/4
-encode(dwr = M, Dict0, Mask) ->
+encode(dwr = M, Dict0, Opts, Mask) ->
Msg = getr(M),
Seq = diameter_session:sequence(Mask),
Hdr = #diameter_header{version = ?DIAMETER_VERSION,
@@ -512,10 +519,10 @@ encode(dwr = M, Dict0, Mask) ->
hop_by_hop_id = Seq},
Pkt = #diameter_packet{header = Hdr,
msg = Msg},
- diameter_codec:encode(Dict0, Pkt);
+ diameter_codec:encode(Dict0, Opts, Pkt);
-encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD}
- = ReqPkt) ->
+encode(dwa, Dict0, Opts, #diameter_packet{header = H, transport_data = TD}
+ = ReqPkt) ->
AnsPkt = #diameter_packet{header
= H#diameter_header{is_request = false,
is_error = undefined,
@@ -523,7 +530,7 @@ encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD}
msg = dwa(ReqPkt),
transport_data = TD},
- diameter_codec:encode(Dict0, AnsPkt).
+ diameter_codec:encode(Dict0, Opts, AnsPkt).
%% okay/3
@@ -593,9 +600,10 @@ tw({M,F,A}) ->
send_watchdog(#watchdog{pending = false,
transport = TPid,
dictionary = Dict0,
- config = #{sequence := Mask}}
+ config = #{sequence := Mask},
+ codec = Opts}
= S) ->
- #diameter_packet{bin = Bin} = EPkt = encode(dwr, Dict0, Mask),
+ #diameter_packet{bin = Bin} = EPkt = encode(dwr, Dict0, Opts, Mask),
diameter_traffic:incr(send, EPkt, TPid, Dict0),
send(TPid, {send, Bin}),
?LOG(send, 'DWR'),
@@ -628,7 +636,7 @@ rcv('DWR', Pkt, #watchdog{transport = TPid,
transport_data = T,
bin = Bin}
= EPkt
- = encode(dwa, Dict0, Pkt),
+ = encode(dwa, Dict0, Opts, Pkt),
diameter_traffic:incr(send, EPkt, TPid, Dict0),
diameter_traffic:incr_rc(send, EPkt, TPid, Dict0),