diff options
author | Anders Svensson <anders@erlang.org> | 2012-08-28 15:05:09 +0200 |
---|---|---|
committer | Anders Svensson <anders@erlang.org> | 2012-08-28 15:05:09 +0200 |
commit | f7036408ed27781cc5d060d6966f07cb2a5e00f6 (patch) | |
tree | 078078420f98329f7a088043a17e1fb9686cb647 | |
parent | d4eccdc4b544215f3706408bdeabe8c231780065 (diff) | |
parent | a907e66c93e54ffd9b4c8c84f500cd267a28ddd8 (diff) | |
download | otp-f7036408ed27781cc5d060d6966f07cb2a5e00f6.tar.gz otp-f7036408ed27781cc5d060d6966f07cb2a5e00f6.tar.bz2 otp-f7036408ed27781cc5d060d6966f07cb2a5e00f6.zip |
Merge branch 'anders/diameter/multiple_transport_modules/OTP-9885' into maint
* anders/diameter/multiple_transport_modules/OTP-9885:
Maintain service_info callback data in process dictionary
Maintain pid of started transport process in process dictionary
Minor spec and backwards compatibility fix
Update example code
Update documentation
Add diameter_transport for transport start indirection
Allow multiple transport_module and transport_config
-rw-r--r-- | lib/diameter/doc/src/diameter.xml | 30 | ||||
-rw-r--r-- | lib/diameter/examples/code/peer.erl | 5 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter.erl | 1 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_peer.erl | 130 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_peer_fsm.erl | 155 | ||||
-rw-r--r-- | lib/diameter/src/modules.mk | 1 | ||||
-rw-r--r-- | lib/diameter/src/transport/diameter_etcp.erl | 22 | ||||
-rw-r--r-- | lib/diameter/src/transport/diameter_sctp.erl | 41 | ||||
-rw-r--r-- | lib/diameter/src/transport/diameter_tcp.erl | 77 | ||||
-rw-r--r-- | lib/diameter/src/transport/diameter_transport.erl | 55 |
10 files changed, 444 insertions, 73 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 93e2603c10..ec7bd2012e 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -764,15 +764,45 @@ Defaults to <c>diameter_tcp</c> if unspecified.</p> <p> The interface required of a transport module is documented in <seealso marker="diameter_transport">diameter_transport(3)</seealso>.</p> + +<p> +Multiple <c>transport_module</c> and <c>transport_config</c> +options are allowed. +The order of these is significant in this case (and only in this case), +a <c>transport_module</c> being paired with the first +<c>transport_config</c> following it in the options list, or the +default value for trailing modules. +Transport starts will be attempted with each of the +modules in order until one establishes a connection within the +corresponding timeout (see <c>transport_config</c> below) or all fail.</p> </item> <tag><c>{transport_config, term()}</c></tag> +<tag><c>{transport_config, term(), Unsigned32()}</c></tag> <item> <p> A term passed as the third argument to the <seealso marker="diameter_transport#start">start/3</seealso> function of the relevant <c>transport_module</c> in order to start a transport process. Defaults to the empty list if unspecified.</p> + +<p> +The 3-tuple form additionally specifies an interval, in milliseconds, +after which a started transport process should be terminated if it has +not yet established a connection. +For example, the following options on a connecting transport +request a connection with one peer over SCTP or another +(typically the same) over TCP.</p> + +<code> +{transport_module, diameter_sctp} +{transport_config, SctpOpts, 5000} +{transport_module, diameter_tcp} +{transport_config, TcpOpts} +</code> + +<p> +To listen on both SCTP and TCP, define one transport for each.</p> </item> <tag><c>{applications, [application_alias()]}</c></tag> diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl index b07cd32b98..8fdeba57bf 100644 --- a/lib/diameter/examples/code/peer.erl +++ b/lib/diameter/examples/code/peer.erl @@ -117,6 +117,11 @@ server(T) -> %% %% Return config for a connecting transport. +client({all, LA, RA, RP}) -> + [[M,{K,C}], T] + = [client({P, LA, RA, RP}) || P <- [sctp,tcp]], + [M, {K,C,2000} | T]; + client({T, LA, RA, RP}) -> [{transport_module, tmod(T)}, {transport_config, [{ip, addr(LA)}, diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 336f0c1f2d..6703841f80 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -312,6 +312,7 @@ call(SvcName, App, Message) -> -type transport_opt() :: {transport_module, atom()} | {transport_config, any()} + | {transport_timeout, non_neg_integer() | infinity} | {applications, [app_alias()]} | {capabilities, [capability()]} | {capabilities_cb, evaluable()} diff --git a/lib/diameter/src/base/diameter_peer.erl b/lib/diameter/src/base/diameter_peer.erl index 3e78c4caef..a2a1c567d8 100644 --- a/lib/diameter/src/base/diameter_peer.erl +++ b/lib/diameter/src/base/diameter_peer.erl @@ -27,12 +27,15 @@ up/2]). %% ... and the stack. --export([start/3, +-export([start/1, send/2, close/1, abort/1, notify/2]). +%% Old interface only called from old code. +-export([start/3]). %% < diameter-1.2 (R15B02) + %% Server start. -export([start_link/0]). @@ -57,6 +60,11 @@ %% Server state. -record(state, {id = now()}). +%% Default transport_module/config. +-define(DEFAULT_TMOD, diameter_tcp). +-define(DEFAULT_TCFG, []). +-define(DEFAULT_TTMO, infinity). + %%% --------------------------------------------------------------------------- %%% # notify/2 %%% --------------------------------------------------------------------------- @@ -68,9 +76,108 @@ notify(SvcName, T) -> %%% # start/3 %%% --------------------------------------------------------------------------- -start(T, Opts, #diameter_service{} = Svc) -> - {Mod, Cfg} = split_transport(Opts), - apply(Mod, start, [T, Svc, Cfg]). +%% From old code: make is restart. +start(_T, _Opts, #diameter_service{}) -> + {error, restart}. + +%%% --------------------------------------------------------------------------- +%%% # start/1 +%%% --------------------------------------------------------------------------- + +-spec start({T, [Opt], #diameter_service{}}) + -> {TPid, [Addr], Tmo, Data} + | {error, [term()]} + when T :: {connect|accept, diameter:transport_ref()}, + Opt :: diameter:transport_opt(), + TPid :: pid(), + Addr :: inet:ip_address(), + Tmo :: non_neg_integer(), + Data :: {{T, Mod, Cfg}, [Mod], [{T, [Mod], Cfg}], [Err]}, + Mod :: module(), + Cfg :: term(), + Err :: term(). + +%% Initial start. +start({T, Opts, #diameter_service{} = Svc}) -> + start(T, Svc, pair(Opts, [], []), []); + +%% Subsequent start. +start({#diameter_service{} = Svc, Tmo, {{T, _, Cfg}, Ms, Rest, Errs}}) -> + start(T, Ms, Cfg, Svc, Tmo, Rest, Errs). + +%% pair/3 +%% +%% Pair transport modules with config. + +%% Another transport_module: accumulate it. +pair([{transport_module, M} | Rest], Mods, Acc) -> + pair(Rest, [M|Mods], Acc); + +%% Another transport_config: accumulate another tuple. +pair([{transport_config = T, C} | Rest], Mods, Acc) -> + pair([{T, C, ?DEFAULT_TTMO} | Rest], Mods, Acc); +pair([{transport_config, C, Tmo} | Rest], Mods, Acc) -> + pair(Rest, [], acc({Mods, C, Tmo}, Acc)); + +pair([_ | Rest], Mods, Acc) -> + pair(Rest, Mods, Acc); + +%% No transport_module or transport_config: defaults. +pair([], [], []) -> + [{[?DEFAULT_TMOD], ?DEFAULT_TCFG, ?DEFAULT_TTMO}]; + +%% One transport_module, one transport_config. +pair([], [M], [{[], Cfg, Tmo}]) -> + [{[M], Cfg, Tmo}]; + +%% Trailing transport_module: default transport_config. +pair([], [_|_] = Mods, Acc) -> + lists:reverse(acc({Mods, ?DEFAULT_TCFG, ?DEFAULT_TTMO}, Acc)); + +pair([], [], Acc) -> + lists:reverse(def(Acc)). + +%% acc/2 + +acc(T, Acc) -> + [T | def(Acc)]. + +%% def/1 +%% +%% Default module of previous pair if none were specified. + +def([{[], Cfg, Tmo} | Acc]) -> + [{[?DEFAULT_TMOD], Cfg, Tmo} | Acc]; +def(Acc) -> + Acc. + +%% start/4 + +start(T, Svc, [{Ms, Cfg, Tmo} | Rest], Errs) -> + start(T, Ms, Cfg, Svc, Tmo, Rest, Errs); + +start(_, _, [], Errs) -> + {error, Errs}. + +%% start/7 + +start(T, [], _, Svc, _, Rest, Errs) -> + start(T, Svc, Rest, Errs); + +start(T, [M|Ms], Cfg, Svc, Tmo, Rest, Errs) -> + case start(M, [T, Svc, Cfg]) of + {ok, TPid} -> + {TPid, [], Tmo, {{T, M, Cfg}, Ms, Rest, Errs}}; + {ok, TPid, [_|_] = Addrs} -> + {TPid, Addrs, Tmo, {{T, M, Cfg}, Ms, Rest, Errs}}; + E -> + start(T, Ms, Cfg, Svc, Tmo, Rest, [E|Errs]) + end. + +%% start/2 + +start(Mod, Args) -> + apply(Mod, start, Args). %%% --------------------------------------------------------------------------- %%% # up/[12] @@ -204,21 +311,6 @@ bang(undefined = No, _) -> bang(Pid, T) -> Pid ! T. -%% split_transport/1 -%% -%% Split options into transport module, transport config and -%% remaining options. - -split_transport(Opts) -> - {[M,C], _} = proplists:split(Opts, [transport_module, - transport_config]), - {value(M, diameter_tcp), value(C, [])}. - -value([{_,V}], _) -> - V; -value([], V) -> - V. - %% call/1 call(Request) -> diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 99644814d2..bbda62c32b 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -54,6 +54,12 @@ -define(NO_INBAND_SECURITY, 0). -define(TLS, 1). +%% Keys in process dictionary. +-define(CB_KEY, cb). %% capabilities callback +-define(DWA_KEY, dwa). %% outgoing DWA +-define(Q_KEY, q). %% transport start queue +-define(START_KEY, start). %% start of connected transport + %% A 2xxx series Result-Code. Not necessarily 2001. -define(IS_SUCCESS(N), 2 == (N) div 1000). @@ -115,6 +121,11 @@ %%% Output: Pid %%% --------------------------------------------------------------------------- +-spec start(T, [Opt], #diameter_service{}) + -> pid() + when T :: {connect|accept, diameter:transport_ref()}, + Opt :: diameter:transport_opt(). + %% diameter_config requires a non-empty list of applications on the %% service but diameter_service then constrains the list to any %% specified on the transport in question. Check here that the list is @@ -143,18 +154,17 @@ init(T) -> proc_lib:init_ack({ok, self()}), gen_server:enter_loop(?MODULE, [], i(T)). -i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) -> - putr(dwa, dwa(Caps)), +i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc}) -> + putr(?DWA_KEY, dwa(Caps)), {M, Ref} = T, {[Ts], Rest} = proplists:split(Opts, [capabilities_cb]), - putr(capabilities_cb, {Ref, [F || {_,F} <- Ts]}), - {ok, TPid, Svc} = start_transport(T, Rest, Svc0), - erlang:monitor(process, TPid), + putr(?CB_KEY, {Ref, [F || {_,F} <- Ts]}), erlang:monitor(process, WPid), + {TPid, Addrs} = start_transport(T, Rest, Svc), #state{parent = WPid, transport = TPid, mode = M, - service = Svc}. + service = svc(Svc, Addrs)}. %% 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 @@ -164,18 +174,56 @@ i({WPid, T, Opts, #diameter_service{capabilities = Caps} = Svc0}) -> %% watchdog start (start/2) succeeds regardless so as not to crash the %% service. -start_transport(T, Opts, Svc) -> - case diameter_peer:start(T, Opts, Svc) of - {ok, TPid} -> - {ok, TPid, Svc}; - {ok, TPid, [_|_] = Addrs} -> - #diameter_service{capabilities = Caps0} = Svc, - Caps = Caps0#diameter_caps{host_ip_address = Addrs}, - {ok, TPid, Svc#diameter_service{capabilities = Caps}}; +start_transport(T, Opts, #diameter_service{capabilities = Caps} = Svc) -> + Addrs0 = Caps#diameter_caps.host_ip_address, + start_transport(Addrs0, {T, Opts, Svc}). + +start_transport(Addrs0, T) -> + case diameter_peer:start(T) of + {TPid, Addrs, Tmo, Data} -> + erlang:monitor(process, TPid), + q_next(TPid, Addrs0, Tmo, Data), + {TPid, addrs(Addrs, Addrs0)}; No -> exit({shutdown, No}) end. +addrs([], Addrs0) -> + Addrs0; +addrs(Addrs, _) -> + Addrs. + +svc(Svc, []) -> + Svc; +svc(Svc, Addrs) -> + readdr(Svc, Addrs). + +readdr(#diameter_service{capabilities = Caps0} = Svc, Addrs) -> + Caps = Caps0#diameter_caps{host_ip_address = Addrs}, + Svc#diameter_service{capabilities = Caps}. + +%% The 4-tuple Data returned from diameter_peer:start/1 identifies the +%% transport module/config use to start the transport process in +%% question as well as any alternates to try if a connection isn't +%% established within Tmo. +q_next(TPid, Addrs0, Tmo, {_,_,_,_} = Data) -> + send_after(Tmo, {connection_timeout, TPid}), + putr(?Q_KEY, {Addrs0, Tmo, Data}). + +%% Connection has been established: retain the started +%% pid/module/config in the process dictionary. This is a part of the +%% interface defined by this module, so that the transport pid can be +%% found when constructing service_info (in order to extract further +%% information from it). +keep_transport(TPid) -> + {_, _, {{_,_,_} = T, _, _, _}} = eraser(?Q_KEY), + putr(?START_KEY, {TPid, T}). + +send_after(infinity, _) -> + ok; +send_after(Tmo, T) -> + erlang:send_after(Tmo, self(), T). + %% handle_call/3 handle_call(_, _, State) -> @@ -240,25 +288,48 @@ eraser(Key) -> %% Connection to peer. transition({diameter, {TPid, connected, Remote}}, - #state{state = PS, + #state{transport = TPid, + state = PS, mode = M} = S) -> 'Wait-Conn-Ack' = PS, %% assert connect = M, %% - send_CER(S#state{mode = {M, Remote}, - transport = TPid}); + keep_transport(TPid), + send_CER(S#state{mode = {M, Remote}}); %% Connection from peer. transition({diameter, {TPid, connected}}, - #state{state = PS, + #state{transport = TPid, + state = PS, mode = M, parent = Pid} = S) -> 'Wait-Conn-Ack' = PS, %% assert accept = M, %% + keep_transport(TPid), Pid ! {accepted, self()}, - start_timer(S#state{state = recv_CER, - transport = TPid}); + start_timer(S#state{state = recv_CER}); + +%% Connection established after receiving a connection_timeout +%% message. This may be followed by an incoming message which arrived +%% before the transport was killed and this can't be distinguished +%% from one from the transport that's been started to replace it. +transition({diameter, {_, connected}}, _) -> + {stop, connection_timeout}; +transition({diameter, {_, connected, _}}, _) -> + {stop, connection_timeout}; + +%% Connection has timed out: start an alternate. +transition({connection_timeout = T, TPid}, + #state{transport = TPid, + state = 'Wait-Conn-Ack'} + = S) -> + exit(TPid, {shutdown, T}), + start_next(S); + +%% Connect timeout after connection or alternate start: ignore. +transition({connection_timeout, _}, _) -> + ok; %% Incoming message from the transport. transition({diameter, {recv, Pkt}}, S) -> @@ -305,14 +376,21 @@ transition({resolve_port, _Pid} = T, #state{transport = TPid}) -> TPid ! T, ok; -%% Parent or transport has died. -transition({'DOWN', _, process, P, _}, - #state{parent = Pid, - transport = TPid}) - when P == Pid; - P == TPid -> +%% Parent has died. +transition({'DOWN', _, process, WPid, _}, + #state{parent = WPid}) -> stop; +%% Transport has died before connection timeout. +transition({'DOWN', _, process, TPid, _}, + #state{transport = TPid} + = S) -> + start_next(S); + +%% Transport has died after connection timeout. +transition({'DOWN', _, process, _, _}, _) -> + ok; + %% State query. transition({state, Pid}, #state{state = S, transport = TPid}) -> Pid ! {self(), [S, TPid]}, @@ -320,6 +398,19 @@ transition({state, Pid}, #state{state = S, transport = TPid}) -> %% Crash on anything unexpected. +%% start_next/1 + +start_next(#state{service = Svc0} = S) -> + case getr(?Q_KEY) of + {Addrs0, Tmo, Data} -> + Svc = readdr(Svc0, Addrs0), + {TPid, Addrs} = start_transport(Addrs0, {Svc, Tmo, Data}), + S#state{transport = TPid, + service = svc(Svc, Addrs)}; + undefined -> + stop + end. + %% send_CER/1 send_CER(#state{mode = {connect, Remote}, @@ -649,7 +740,7 @@ rc([RC|_]) -> %% answer/2 answer('DWR', _) -> - getr(dwa); + getr(?DWA_KEY); answer(Name, #state{service = #diameter_service{capabilities = Caps}}) -> a(Name, Caps). @@ -749,15 +840,15 @@ caps(#state{service = Svc}) -> %% caps_cb/1 caps_cb(Caps) -> - {Ref, Ts} = eraser(capabilities_cb), - ccb(Ts, [Ref, Caps]). + {Ref, Ts} = eraser(?CB_KEY), + caps_cb(Ts, [Ref, Caps]). -ccb([], _) -> +caps_cb([], _) -> ok; -ccb([F | Rest], T) -> +caps_cb([F | Rest], T) -> case diameter_lib:eval([F|T]) of ok -> - ccb(Rest, T); + caps_cb(Rest, T); N when ?IS_SUCCESS(N) -> %% 2xxx result code: accept immediately N; Res -> diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index 7a700a6d53..5d3c4157ae 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -58,6 +58,7 @@ RT_MODULES = \ transport/diameter_tcp_sup \ transport/diameter_sctp \ transport/diameter_sctp_sup \ + transport/diameter_transport \ transport/diameter_transport_sup # Handwritten (compile time) modules not included in the app file. diff --git a/lib/diameter/src/transport/diameter_etcp.erl b/lib/diameter/src/transport/diameter_etcp.erl index d925d62545..cd62cf34fa 100644 --- a/lib/diameter/src/transport/diameter_etcp.erl +++ b/lib/diameter/src/transport/diameter_etcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -36,7 +36,9 @@ send/2, close/1, setopts/2, - port/1]). + sockname/1, + peername/1, + getstat/1]). %% child start -export([start_link/1]). @@ -113,10 +115,20 @@ close(Pid) -> setopts(_, _) -> ok. -%% port/1 +%% sockname/1 -port(_) -> - 3868. %% We have no local port: fake it. +sockname(_) -> + {error, ?MODULE}. + +%% peername/1 + +peername(_) -> + {error, ?MODULE}. + +%% getstat/1 + +getstat(_) -> + {error, ?MODULE}. %% start_link/1 diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 68b0342cd5..9a65834647 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,12 +37,18 @@ code_change/3, terminate/2]). +-export([info/1]). %% service_info callback + -export([ports/0, ports/1]). -include_lib("kernel/include/inet_sctp.hrl"). -include_lib("diameter/include/diameter.hrl"). +%% Keys into process dictionary. +-define(INFO_KEY, info). +-define(REF_KEY, ref). + -define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})). %% The default port for a listener. @@ -134,6 +140,24 @@ start_link(T) -> diameter_lib:spawn_opts(server, [])). %% --------------------------------------------------------------------------- +%% # info/1 +%% --------------------------------------------------------------------------- + +info({gen_sctp, Sock}) -> + lists:flatmap(fun(K) -> info(K, Sock) end, + [{socket, sockname}, + {peer, peername}, + {statistics, getstat}]). + +info({K,F}, Sock) -> + case inet:F(Sock) of + {ok, V} -> + [{K,V}]; + _ -> + [] + end. + +%% --------------------------------------------------------------------------- %% # init/1 %% --------------------------------------------------------------------------- @@ -157,7 +181,7 @@ i({connect, Pid, Opts, Addrs, Ref}) -> RAs = [diameter_lib:ipaddr(A) || {raddr, A} <- As], [RP] = [P || {rport, P} <- Ps] ++ [P || P <- [?DEFAULT_PORT], [] == Ps], {LAs, Sock} = open(Addrs, Rest, 0), - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self(), LAs}), erlang:monitor(process, Pid), #transport{parent = Pid, @@ -169,7 +193,7 @@ i({connect, _, _, _} = T) -> %% from old code %% An accepting transport spawned by diameter. i({accept, Pid, LPid, Sock, Ref}) when is_pid(Pid) -> - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self()}), erlang:monitor(process, Pid), erlang:monitor(process, LPid), @@ -181,7 +205,7 @@ i({accept, _, _, _} = T) -> %% from old code %% An accepting transport spawned at association establishment. i({accept, Ref, LPid, Sock, Id}) -> - putr(ref, Ref), + putr(?REF_KEY, Ref), proc_lib:init_ack({ok, self()}), MRef = erlang:monitor(process, LPid), %% Wait for a signal that the transport has been started before @@ -554,10 +578,9 @@ recv({_, #sctp_assoc_change{state = comm_up, mode = {T, _}, socket = Sock} = S) -> - Ref = getr(ref), + Ref = getr(?REF_KEY), is_reference(Ref) %% started in new code - andalso - (true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}})), + andalso publish(T, Ref, Id, Sock), up(S#transport{assoc_id = Id, streams = {IS, OS}}); @@ -599,6 +622,10 @@ recv({_, #sctp_paddr_change{}}, _) -> recv({_, #sctp_pdapi_event{}}, _) -> ok. +publish(T, Ref, Id, Sock) -> + true = diameter_reg:add_new({?MODULE, T, {Ref, {Id, Sock}}}), + putr(?INFO_KEY, {gen_sctp, Sock}). %% for info/1 + %% up/1 up(#transport{parent = Pid, diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index 78dbda6888..597f2f14d7 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,11 +37,17 @@ code_change/3, terminate/2]). +-export([info/1]). %% service_info callback + -export([ports/0, ports/1]). -include_lib("diameter/include/diameter.hrl"). +%% Keys into process dictionary. +-define(INFO_KEY, info). +-define(REF_KEY, ref). + -define(ERROR(T), erlang:error({T, ?MODULE, ?LINE})). -define(DEFAULT_PORT, 3868). %% RFC 3588, ch 2.1 @@ -111,6 +117,33 @@ start_link(T) -> diameter_lib:spawn_opts(server, [])). %% --------------------------------------------------------------------------- +%% # info/1 +%% --------------------------------------------------------------------------- + +info({Mod, Sock}) -> + lists:flatmap(fun(K) -> info(Mod, K, Sock) end, + [{socket, fun sockname/2}, + {peer, fun peername/2}, + {statistics, fun getstat/2} + | ssl_info(Mod, Sock)]). + +info(Mod, {K,F}, Sock) -> + case F(Mod, Sock) of + {ok, V} -> + [{K,V}]; + _ -> + [] + end. + +ssl_info(ssl = M, Sock) -> + [{M, ssl_info(Sock)}]; +ssl_info(_, _) -> + []. + +ssl_info(Sock) -> + [{peercert, C} || {ok, C} <- [ssl:peercert(Sock)]]. + +%% --------------------------------------------------------------------------- %% # init/1 %% --------------------------------------------------------------------------- @@ -133,7 +166,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) MPid ! {stop, self()}, %% tell the monitor to die M = if SslOpts -> ssl; true -> Mod end, setopts(M, Sock), - putr(ref, Ref), + putr(?REF_KEY, Ref), #transport{parent = Pid, module = M, socket = Sock, @@ -191,7 +224,7 @@ i(accept = T, Ref, Mod, Pid, Opts, Addrs) -> {LAddr, LSock} = listener(Ref, {Mod, Opts, Addrs}), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(accept(Mod, LSock)), - true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + publish(Mod, T, Ref, Sock), diameter_peer:up(Pid), Sock; @@ -202,10 +235,14 @@ i(connect = T, Ref, Mod, Pid, Opts, Addrs) -> RPort = get_port(RP), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(connect(Mod, RAddr, RPort, gen_opts(LAddr, Rest))), - true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + publish(Mod, T, Ref, Sock), diameter_peer:up(Pid, {RAddr, RPort}), Sock. +publish(Mod, T, Ref, Sock) -> + true = diameter_reg:add_new({?MODULE, T, {Ref, Sock}}), + putr(?INFO_KEY, {Mod, Sock}). %% for info/1 + ok({ok, T}) -> T; ok(No) -> @@ -521,7 +558,7 @@ tls_handshake(Type, true, #transport{socket = Sock, ssl = Opts} = S) -> {ok, SSock} = tls(Type, Sock, [{cb_info, ?TCP_CB(M)} | Opts]), - Ref = getr(ref), + Ref = getr(?REF_KEY), is_reference(Ref) %% started in new code andalso (true = diameter_reg:add_new({?MODULE, Type, {Ref, SSock}})), @@ -696,12 +733,32 @@ setopts(M, Sock) -> portnr(gen_tcp, Sock) -> inet:port(Sock); -portnr(ssl, Sock) -> - case ssl:sockname(Sock) of +portnr(M, Sock) -> + case M:sockname(Sock) of {ok, {_Addr, PortNr}} -> {ok, PortNr}; {error, _} = No -> No - end; -portnr(M, Sock) -> - M:port(Sock). + end. + +%% sockname/2 + +sockname(gen_tcp, Sock) -> + inet:sockname(Sock); +sockname(M, Sock) -> + M:sockname(Sock). + +%% peername/2 + +peername(gen_tcp, Sock) -> + inet:peername(Sock); +peername(M, Sock) -> + M:peername(Sock). + +%% getstat/2 + +getstat(gen_tcp, Sock) -> + inet:getstat(Sock); +getstat(M, Sock) -> + M:getstat(Sock). +%% Note that ssl:getstat/1 doesn't yet exist in R15B01. diff --git a/lib/diameter/src/transport/diameter_transport.erl b/lib/diameter/src/transport/diameter_transport.erl new file mode 100644 index 0000000000..ff4b6bbc6d --- /dev/null +++ b/lib/diameter/src/transport/diameter_transport.erl @@ -0,0 +1,55 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(diameter_transport). + +%% +%% This module implements a transport start function that +%% evaluates its config argument. +%% + +%% Transport start functions +-export([start/3, + select/3, + eval/3]). + +%% start/3 + +%% Call a start function in this module ... +start(T, Svc, {F,A}) -> + start(T, Svc, {?MODULE, F, [A]}); + +%% ... or some other. +start(T, Svc, F) -> + diameter_lib:eval([F, T, Svc]). + +%% select/3 +%% +%% A start function that whose config argument is expected to return a +%% new start function. + +select(T, Svc, F) -> + start(T, Svc, diameter_lib:eval([F, T, Svc])). + +%% eval/3 +%% +%% A start function that simply evaluates its config argument. + +eval(_, _, F) -> + diameter_lib:eval(F). |