aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src/base
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/src/base')
-rw-r--r--lib/diameter/src/base/diameter_codec.erl30
-rw-r--r--lib/diameter/src/base/diameter_peer_fsm.erl2
-rw-r--r--lib/diameter/src/base/diameter_service.erl65
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl93
4 files changed, 148 insertions, 42 deletions
diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl
index 06a4f5de64..a2b04bfd63 100644
--- a/lib/diameter/src/base/diameter_codec.erl
+++ b/lib/diameter/src/base/diameter_codec.erl
@@ -237,15 +237,35 @@ rec2msg(Mod, Rec) ->
%% Unsuccessfully decoded AVPs will be placed in #diameter_packet.errors.
--spec decode(module(), #diameter_packet{} | binary())
+-spec decode(module() | {module(), module()}, #diameter_packet{} | binary())
-> #diameter_packet{}.
+%% An Answer setting the E-bit. The application dictionary is needed
+%% for the best-effort decode of Failed-AVP, and the best way to make
+%% this available to the AVP decode in diameter_gen.hrl, without
+%% having to rewrite the entire codec generation, is to place it in
+%% the process dictionary. It's the code in diameter_gen.hrl (that's
+%% included by every generated codec module) that looks for the entry.
+%% Not ideal, but it solves the problem relatively simply.
+decode({Mod, Mod}, Pkt) ->
+ decode(Mod, Pkt);
+decode({Mod, AppMod}, Pkt) ->
+ Key = {?MODULE, dictionary},
+ put(Key, AppMod),
+ try
+ decode(Mod, Pkt)
+ after
+ erase(Key)
+ end;
+
+%% Or not: a request, or an answer not setting the E-bit.
decode(Mod, Pkt) ->
decode(Mod:id(), Mod, Pkt).
-%% If we're a relay application then just extract the avp's without
-%% any decoding of their data since we don't know the application in
-%% question.
+%% decode/3
+
+%% Relay application: just extract the avp's without any decoding of
+%% their data since we don't know the application in question.
decode(?APP_ID_RELAY, _, #diameter_packet{} = Pkt) ->
case collect_avps(Pkt) of
{E, As} ->
@@ -274,6 +294,8 @@ decode(Id, Mod, Bin)
when is_binary(Bin) ->
decode(Id, Mod, #diameter_packet{header = decode_header(Bin), bin = Bin}).
+%% decode_avps/4
+
decode_avps(MsgName, Mod, Pkt, {E, Avps}) ->
?LOG(invalid_avp_length, Pkt#diameter_packet.header),
#diameter_packet{errors = Failed}
diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl
index 31e570ae20..86fc43cdc5 100644
--- a/lib/diameter/src/base/diameter_peer_fsm.erl
+++ b/lib/diameter/src/base/diameter_peer_fsm.erl
@@ -477,6 +477,7 @@ send_CER(#state{state = {'Wait-Conn-Ack', Tmo},
hop_by_hop_id = Hid}}
= Pkt
= encode(CER, Dict),
+ incr(send, Pkt, Dict),
send(TPid, Pkt),
?LOG(send, 'CER'),
start_timer(Tmo, S#state{state = {'Wait-CEA', Hid, Eid}}).
@@ -1100,6 +1101,7 @@ send_dpr(Reason, Opts, #state{transport = TPid,
{'Origin-Realm', OR},
{'Disconnect-Cause', Cause}],
Dict),
+ incr(send, Pkt, Dict),
send(TPid, Pkt),
dpa_timer(Tmo),
?LOG(send, 'DPR'),
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index b7cd311e02..ab56ca9cef 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1573,7 +1573,8 @@ transports(#state{watchdogT = WatchdogT}) ->
-define(OTHER_INFO, [connections,
name,
peers,
- statistics]).
+ statistics,
+ info]).
service_info(Item, S)
when is_atom(Item) ->
@@ -1663,6 +1664,7 @@ complete_info(Item, #state{service = Svc} = S) ->
keys -> ?ALL_INFO ++ ?CAP_INFO ++ ?OTHER_INFO;
all -> service_info(?ALL_INFO, S);
statistics -> info_stats(S);
+ info -> info_info(S);
connections -> info_connections(S);
peers -> info_peers(S)
end.
@@ -1745,12 +1747,11 @@ peer_acc(PeerT, Acc, #watchdog{pid = Pid,
state = WS,
started = At,
peer = TPid}) ->
- dict:append(Ref,
- [{type, Type},
- {options, Opts},
- {watchdog, {Pid, At, WS}}
- | info_peer(PeerT, TPid, WS)],
- Acc).
+ Info = [{type, Type},
+ {options, Opts},
+ {watchdog, {Pid, At, WS}}
+ | info_peer(PeerT, TPid, WS)],
+ dict:append(Ref, Info ++ [{info, info_process_info(Info)}], Acc).
info_peer(PeerT, TPid, WS)
when is_pid(TPid), WS /= ?WD_DOWN ->
@@ -1762,6 +1763,49 @@ info_peer(PeerT, TPid, WS)
info_peer(_, _, _) ->
[].
+info_process_info(Info) ->
+ lists:flatmap(fun ipi/1, Info).
+
+ipi({watchdog, {Pid, _, _}}) ->
+ info_pid(Pid);
+
+ipi({peer, {Pid, _}}) ->
+ info_pid(Pid);
+
+ipi({port, [{owner, Pid} | _]}) ->
+ info_pid(Pid);
+
+ipi(_) ->
+ [].
+
+info_pid(Pid) ->
+ case process_info(Pid, [message_queue_len, memory, binary]) of
+ undefined ->
+ [];
+ L ->
+ [{Pid, lists:map(fun({K,V}) -> {K, map_info(K,V)} end, L)}]
+ end.
+
+%% The binary list consists of 3-tuples {Ptr, Size, Count}, where Ptr
+%% is a C pointer value, Size is the size of a referenced binary in
+%% bytes, and Count is a global reference count. The same Ptr can
+%% occur multiple times, once for each reference on the process heap.
+%% In this case, the corresponding tuples will have Size in common but
+%% Count may differ just because no global lock is taken when the
+%% value is retrieved.
+%%
+%% The list can be quite large, and we aren't often interested in the
+%% pointers or counts, so whittle this down to the number of binaries
+%% referenced and their total byte count.
+map_info(binary, L) ->
+ SzD = lists:foldl(fun({P,S,_}, D) -> dict:store(P,S,D) end,
+ dict:new(),
+ L),
+ {dict:size(SzD), dict:fold(fun(_,S,N) -> S + N end, 0, SzD)};
+
+map_info(_, T) ->
+ T.
+
%% The point of extracting the config here is so that 'transport' info
%% has one entry for each transport ref, the peer table only
%% containing entries that have a living watchdog.
@@ -1819,6 +1863,13 @@ mk_app(#diameter_app{} = A) ->
info_pending(#state{} = S) ->
diameter_traffic:pending(transports(S)).
+%% info_info/1
+%%
+%% Extract process_info from connections info.
+
+info_info(S) ->
+ [I || L <- conn_list(S), {info, I} <- L].
+
%% info_connections/1
%%
%% One entry per transport connection. Statistics for each entry are
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 5fac61f416..280d09d7e8 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -129,6 +129,11 @@ incr(Dir, #diameter_header{} = H, TPid, Dict) ->
%% incr_error/4
%% ---------------------------------------------------------------------------
+%% Identify messages using the application dictionary, not the encode
+%% dictionary, which may differ in the case of answer-message.
+incr_error(Dir, T, Pid, {_Dict, AppDict}) ->
+ incr_error(Dir, T, Pid, AppDict);
+
%% Decoded message without errors.
incr_error(recv, #diameter_packet{errors = []}, _, _) ->
ok;
@@ -169,7 +174,7 @@ incr_error(Dir, Id, TPid) ->
incr_rc(Dir, Pkt, TPid, Dict0) ->
try
- incr_rc(Dir, Pkt, Dict0, TPid, Dict0)
+ incr_result(Dir, Pkt, TPid, {Dict0, Dict0, Dict0})
catch
exit: {E,_} when E == no_result_code;
E == invalid_error_bit ->
@@ -471,7 +476,7 @@ 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, Dict0},
Pkt,
[],
[]);
@@ -479,7 +484,7 @@ send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application
send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) ->
send_A(answer(T, Caps, Pkt, App, Dict0, RecvData),
TPid,
- Dict0,
+ {App#diameter_app.dictionary, Dict0},
Pkt,
EvalPktFs,
EvalFs);
@@ -489,8 +494,8 @@ send_A(_, _, _, _) ->
%% send_A/6
-send_A(T, TPid, Dict0, ReqPkt, EvalPktFs, EvalFs) ->
- reply(T, TPid, Dict0, EvalPktFs, ReqPkt),
+send_A(T, TPid, DictT, ReqPkt, EvalPktFs, EvalFs) ->
+ reply(T, TPid, DictT, EvalPktFs, ReqPkt),
lists:foreach(fun diameter_lib:eval/1, EvalFs).
%% answer/6
@@ -648,32 +653,32 @@ is_loop(Code, Vid, OH, Dict0, Avps) ->
%% reply/5
%% Local answer ...
-reply({Dict, Ans}, TPid, Dict0, Fs, ReqPkt) ->
- reply(Ans, Dict, TPid, Dict0, Fs, ReqPkt);
+reply({Dict, Ans}, TPid, {AppDict, Dict0}, Fs, ReqPkt) ->
+ local(Ans, TPid, {Dict, AppDict, Dict0}, Fs, ReqPkt);
%% ... or relayed.
reply(#diameter_packet{} = Pkt, TPid, _Dict0, Fs, _ReqPkt) ->
eval_packet(Pkt, Fs),
send(TPid, Pkt).
-%% reply/6
+%% local/5
%%
%% Send a locally originating reply.
%% Skip the setting of Result-Code and Failed-AVP's below. This is
%% undocumented and shouldn't be relied on.
-reply([Msg], Dict, TPid, Dict0, Fs, ReqPkt)
+local([Msg], TPid, DictT, Fs, ReqPkt)
when is_list(Msg);
is_tuple(Msg) ->
- reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt#diameter_packet{errors = []});
+ local(Msg, TPid, DictT, Fs, ReqPkt#diameter_packet{errors = []});
-reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt) ->
- Pkt = encode(Dict,
+local(Msg, TPid, {Dict, AppDict, Dict0} = DictT, Fs, ReqPkt) ->
+ Pkt = encode({Dict, AppDict},
TPid,
reset(make_answer_packet(Msg, ReqPkt), Dict, Dict0),
Fs),
- incr(send, Pkt, TPid, Dict),
- incr_rc(send, Pkt, Dict, TPid, Dict0), %% count outgoing
+ incr(send, Pkt, TPid, AppDict),
+ incr_result(send, Pkt, TPid, DictT), %% count outgoing
send(TPid, Pkt).
%% reset/3
@@ -1038,29 +1043,29 @@ find(Pred, [H|T]) ->
%% code, the missing vendor id, and a zero filled payload of the minimum
%% required length for the omitted AVP will be added.
-%% incr_rc/5
+%% incr_result/5
%%
%% Increment a stats counter for result codes in incoming and outgoing
%% answers.
%% Outgoing message as binary: don't count. (Sending binaries is only
%% partially supported.)
-incr_rc(_, #diameter_packet{msg = undefined = No}, _, _, _) ->
+incr_result(_, #diameter_packet{msg = undefined = No}, _, _) ->
No;
%% Incoming or outgoing. Outgoing with encode errors never gets here
%% since encode fails.
-incr_rc(Dir, Pkt, Dict, TPid, Dict0) ->
+incr_result(Dir, Pkt, TPid, {Dict, AppDict, Dict0}) ->
#diameter_packet{header = #diameter_header{is_error = E}
= Hdr,
msg = Msg,
errors = Es}
= Pkt,
- Id = msg_id(Hdr, Dict),
+ Id = msg_id(Hdr, AppDict),
%% Count incoming decode errors.
- recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, Dict),
+ recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict),
%% Exit on a missing result code.
T = rc_counter(Dict, Msg),
@@ -1074,12 +1079,27 @@ incr_rc(Dir, Pkt, Dict, TPid, Dict0) ->
incr(TPid, {Id, Dir, Ctr}),
Ctr.
-%% Only count on known keeps so as not to be vulnerable to attack:
-%% there are 2^32 (application ids) * 2^24 (command codes) * 2 (R-bits)
-%% = 2^57 Ids for an attacker to choose from.
+%% msg_id/2
+
+msg_id(#diameter_packet{header = H}, Dict) ->
+ msg_id(H, Dict);
+
+%% Only count on known keys so as not to be vulnerable to attack:
+%% there are 2^32 (application ids) * 2^24 (command codes) = 2^56
+%% pairs for an attacker to choose from.
msg_id(Hdr, Dict) ->
{_ApplId, Code, R} = Id = diameter_codec:msg_id(Hdr),
- choose('' == Dict:msg_name(Code, 0 == R), unknown, Id).
+ case Dict:msg_name(Code, 0 == R) of
+ '' ->
+ unknown(Dict:id(), R);
+ _ ->
+ Id
+ end.
+
+unknown(?APP_ID_RELAY, R) ->
+ {relay, R};
+unknown(_, _) ->
+ unknown.
%% No E-bit: can't be 3xxx.
is_result(RC, false, _Dict0) ->
@@ -1396,6 +1416,7 @@ send_R(Pkt0,
packet = Pkt0},
try
+ incr(send, Pkt, TPid, Dict),
TRef = send_request(TPid, Pkt, Req, SvcName, Timeout),
Pid ! Ref, %% tell caller a send has been attempted
handle_answer(SvcName,
@@ -1431,14 +1452,14 @@ handle_answer(SvcName, App, {error, Req, Reason}) ->
handle_error(App, Req, Reason, SvcName);
handle_answer(SvcName,
- #diameter_app{dictionary = Dict,
+ #diameter_app{dictionary = AppDict,
id = Id}
= App,
{answer, Req, Dict0, Pkt}) ->
- Mod = dict(Dict, Dict0, Pkt),
- handle_A(errors(Id, diameter_codec:decode(Mod, Pkt)),
+ Dict = dict(AppDict, Dict0, Pkt),
+ handle_A(errors(Id, diameter_codec:decode({Dict, AppDict}, Pkt)),
SvcName,
- Mod,
+ Dict,
Dict0,
App,
Req).
@@ -1448,10 +1469,12 @@ handle_answer(SvcName,
%% want to examine the answer?
handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
- incr(recv, Pkt, TPid, Dict),
+ AppDict = App#diameter_app.dictionary,
+
+ incr(recv, Pkt, TPid, AppDict),
try
- incr_rc(recv, Pkt, Dict, TPid, Dict0) %% count incoming
+ incr_result(recv, Pkt, TPid, {Dict, AppDict, Dict0}) %% count incoming
of
_ -> answer(Pkt, SvcName, App, Req)
catch
@@ -1468,6 +1491,8 @@ handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
answer(Pkt#diameter_packet{errors = [E|Es]}, SvcName, App, Req)
end.
+%% answer/4
+
answer(Pkt,
SvcName,
#diameter_app{module = ModX,
@@ -1568,13 +1593,18 @@ encode(Dict, TPid, Pkt, Fs) ->
%% an encoded binary. This isn't the usual case and doesn't properly
%% support retransmission but is useful for test.
+encode(Dict, TPid, Pkt)
+ when is_atom(Dict) ->
+ encode({Dict, Dict}, TPid, Pkt);
+
%% A message to be encoded.
-encode(Dict, TPid, #diameter_packet{bin = undefined} = Pkt) ->
+encode(DictT, TPid, #diameter_packet{bin = undefined} = Pkt) ->
+ {Dict, AppDict} = DictT,
try
diameter_codec:encode(Dict, Pkt)
catch
exit: {diameter_codec, encode, T} = Reason ->
- incr_error(send, T, TPid, Dict),
+ incr_error(send, T, TPid, AppDict),
exit(Reason)
end;
@@ -1683,6 +1713,7 @@ resend_request(Pkt0,
caps = Caps},
?LOG(retransmission, Pkt#diameter_packet.header),
+ incr(TPid, {msg_id(Pkt, Dict), send, retransmission}),
TRef = send_request(TPid, Pkt, Req, SvcName, Tmo),
{TRef, Req}.