From 345f11bf3de62b7b4161cc77ed2942ec5e5d9932 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Tue, 16 Apr 2024 15:07:20 +0200 Subject: [PATCH] [client] split socket manager from sending functionality Seperate concerns better. Socket managing is not in the mngr module, while the sending logic is in the client module. --- src/eradius.erl | 2 +- src/eradius_client.erl | 382 +++++------------------------ src/eradius_client_mngr.erl | 385 ++++++++++++++++++++++++++++++ src/eradius_client_socket.erl | 28 +-- src/eradius_client_socket_sup.erl | 52 ++++ src/eradius_client_sup.erl | 46 ++-- src/eradius_internal.hrl | 2 + src/eradius_proxy.erl | 10 +- src/eradius_server.erl | 18 +- src/eradius_sup.erl | 18 +- test/eradius_client_SUITE.erl | 28 +-- test/eradius_metrics_SUITE.erl | 2 +- 12 files changed, 589 insertions(+), 384 deletions(-) create mode 100644 src/eradius_client_mngr.erl create mode 100644 src/eradius_client_socket_sup.erl create mode 100644 src/eradius_internal.hrl diff --git a/src/eradius.erl b/src/eradius.erl index dbd1e473..7bc497e0 100644 --- a/src/eradius.erl +++ b/src/eradius.erl @@ -62,7 +62,7 @@ config_change(Added, Changed, Removed) -> Keys = [K || {K, _} <- Added ++ Changed] ++ Removed, (lists:member(logging, Keys) or lists:member(logfile, Keys)) andalso eradius_log:reconfigure(), - eradius_client:reconfigure(). + eradius_client_mngr:reconfigure(). do_config_change({tables, NewTables}) -> eradius_dict:load_tables(NewTables); diff --git a/src/eradius_client.erl b/src/eradius_client.erl index b4212ba0..4ab7a473 100644 --- a/src/eradius_client.erl +++ b/src/eradius_client.erl @@ -1,3 +1,8 @@ +%% Copyright (c) 2002-2007, Martin Björklund and Torbjörn Törnkvist +%% Copyright (c) 2011, Travelping GmbH +%% +%% SPDX-License-Identifier: MIT +%% %% @doc This module contains a RADIUS client that can be used to send authentication and accounting requests. %% A counter is kept for every NAS in order to determine the next request id and sender port %% for each outgoing request. The implementation naively assumes that you won't send requests to a @@ -12,31 +17,22 @@ %% or the atom ``undefined'' (the default), which uses whatever address the OS selects. -module(eradius_client). --export([start_link/0, send_request/2, send_request/3, send_remote_request/3, send_remote_request/4]). - -%% internal --export([reconfigure/0, send_remote_request_loop/8, find_suitable_peer/1, - restore_upstream_server/1, store_radius_server_from_pool/3, - init_server_status_metrics/0]). +%% API +-export([send_request/2, send_request/3, + send_remote_request/3, send_remote_request/4]). --behaviour(gen_server). --export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +%% internal API +-export([send_remote_request_loop/8]). -import(eradius_lib, [printable_peer/2]). --ifdef(TEST). --export([get_state/0]). --endif. - -include_lib("stdlib/include/ms_transform.hrl"). -include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/include/inet.hrl"). -include("eradius_dict.hrl"). -include("eradius_lib.hrl"). +-include("eradius_internal.hrl"). --define(SERVER, ?MODULE). --define(DEFAULT_RETRIES, 3). --define(DEFAULT_TIMEOUT, 5000). --define(RECONFIGURE_TIMEOUT, 15000). -define(GOOD_CMD(Req), (Req#radius_request.cmd == 'request' orelse Req#radius_request.cmd == 'accreq' orelse Req#radius_request.cmd == 'coareq' orelse @@ -52,13 +48,11 @@ -export_type([nas_address/0, options/0]). --include_lib("kernel/include/inet.hrl"). +-define(SERVER, ?MODULE). -%% ------------------------------------------------------------------------------------------ -%% -- API -%% @private -start_link() -> - gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). +%%%========================================================================= +%%% API +%%%========================================================================= %% @equiv send_request(NAS, Request, []) -spec send_request(nas_address(), #radius_request{}) -> {ok, binary()} | {error, 'timeout' | 'socket_down'}. @@ -85,7 +79,7 @@ send_request({IP, Port, Secret}, Request, Options) when ?GOOD_CMD(Request) andal SendReqFn = fun () -> Peer = {ServerName, {IP, Port}}, update_client_requests(MetricsInfo), - {Socket, ReqId} = gen_server:call(?SERVER, {wanna_send, Peer, MetricsInfo}), + {Socket, ReqId} = eradius_client_mngr:wanna_send(Peer), Response = send_request_loop(Socket, ReqId, Peer, Request#radius_request{reqid = ReqId, secret = Secret}, Retries, Timeout, MetricsInfo), @@ -98,7 +92,7 @@ send_request({IP, Port, Secret}, Request, Options) when ?GOOD_CMD(Request) andal [] -> SendReqFn(); UpstreamServers -> - case find_suitable_peer([{IP, Port, Secret} | UpstreamServers]) of + case eradius_client_mngr:find_suitable_peer([{IP, Port, Secret} | UpstreamServers]) of [] -> no_active_servers; {{IP, Port, Secret}, _NewPool} -> @@ -133,7 +127,7 @@ send_remote_request(Node, {IP, Port, Secret}, Request, Options) when ?GOOD_CMD(R MetricsInfo = make_metrics_info(Options, {IP, Port}), update_client_requests(MetricsInfo), Peer = {ServerName, {IP, Port}}, - try gen_server:call({?SERVER, Node}, {wanna_send, Peer, MetricsInfo}) of + try eradius_client_mngr:wanna_send(Node, Peer) of {Socket, ReqId} -> Request1 = case eradius_node_mon:get_remote_version(Node) of {0, Minor} when Minor < 6 -> @@ -162,29 +156,40 @@ send_remote_request(Node, {IP, Port, Secret}, Request, Options) when ?GOOD_CMD(R send_remote_request(_Node, {_IP, _Port, _Secret}, _Request, _Options) -> error(badarg). -restore_upstream_server({ServerIP, Port, Retries, InitialRetries}) -> - ets:insert(?MODULE, {{ServerIP, Port}, Retries, InitialRetries}). - proceed_response(Request, {ok, Response, Secret, Authenticator}, _Peer = {_ServerName, {ServerIP, Port}}, TS1, MetricsInfo, Options) -> update_client_request(Request#radius_request.cmd, MetricsInfo, erlang:monotonic_time() - TS1, Request), update_client_responses(MetricsInfo), case eradius_lib:decode_request(Response, Secret, Authenticator) of - {bad_pdu, "Message-Authenticator Attribute is invalid" = Reason} -> - update_client_response(bad_authenticator, MetricsInfo, Request), - ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]), - noreply; - {bad_pdu, "Authenticator Attribute is invalid" = Reason} -> - update_client_response(bad_authenticator, MetricsInfo, Request), - ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]), - noreply; - {bad_pdu, "unknown request type" = Reason} -> - update_client_response(unknown_req_type, MetricsInfo, Request), - ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]), - noreply; {bad_pdu, Reason} -> - update_client_response(dropped, MetricsInfo, Request), - ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]), - maybe_failover(Request, noreply, {ServerIP, Port}, Options); + eradius_client_mngr:request_failed(ServerIP, Port, Options), + update_server_status_metric(ServerIP, Port, false, Options), + + case Reason of + "Message-Authenticator Attribute is invalid" -> + update_client_response(bad_authenticator, MetricsInfo, Request), + ?LOG(error, "~s INF: Noreply for request ~p. " + "Message-Authenticator Attribute is invalid", + [printable_peer(ServerIP, Port), Request]), + noreply; + "Authenticator Attribute is invalid" -> + update_client_response(bad_authenticator, MetricsInfo, Request), + ?LOG(error, "~s INF: Noreply for request ~p. " + "Authenticator Attribute is invalid", + [printable_peer(ServerIP, Port), Request]), + noreply; + "unknown request type" -> + update_client_response(unknown_req_type, MetricsInfo, Request), + ?LOG(error, "~s INF: Noreply for request ~p. " + "unknown request type", + [printable_peer(ServerIP, Port), Request]), + noreply; + _ -> + update_client_response(dropped, MetricsInfo, Request), + ?LOG(error, "~s INF: Noreply for request ~p. " + "Could not decode the request, reason: ~s", + [printable_peer(ServerIP, Port), Request, Reason]), + maybe_failover(Request, noreply, Options) + end; Decoded -> update_server_status_metric(ServerIP, Port, true, Options), update_client_response(Decoded#radius_request.cmd, MetricsInfo, Request), @@ -194,39 +199,15 @@ proceed_response(Request, {ok, Response, Secret, Authenticator}, _Peer = {_Serve proceed_response(Request, Response, {_ServerName, {ServerIP, Port}}, TS1, MetricsInfo, Options) -> update_client_responses(MetricsInfo), update_client_request(Request#radius_request.cmd, MetricsInfo, erlang:monotonic_time() - TS1, Request), - maybe_failover(Request, Response, {ServerIP, Port}, Options). -maybe_failover(Request, Response, {ServerIP, Port}, Options) -> + eradius_client_mngr:request_failed(ServerIP, Port, Options), update_server_status_metric(ServerIP, Port, false, Options), - case proplists:get_value(failover, Options, []) of - [] -> - Response; - UpstreamServers -> - handle_failed_request(Request, {ServerIP, Port}, UpstreamServers, Response, Options) - end. -handle_failed_request(Request, {ServerIP, Port} = _FailedServer, UpstreamServers, Response, Options) -> - case ets:lookup(?MODULE, {ServerIP, Port}) of - [{{ServerIP, Port}, Retries, InitialRetries}] -> - FailedTries = proplists:get_value(retries, Options, ?DEFAULT_RETRIES), - %% Mark the given RADIUS server as 'non-active' if there were more tries - %% than possible - if FailedTries >= Retries -> - ets:delete(?MODULE, {ServerIP, Port}), - Timeout = application:get_env(eradius, unreachable_timeout, 60), - timer:apply_after(Timeout * 1000, ?MODULE, restore_upstream_server, - [{ServerIP, Port, InitialRetries, InitialRetries}]); - true -> - %% RADIUS client tried to send a request to the {ServierIP, Port} RADIUS - %% server. There were done FailedTries tries and all of them failed. - %% So decrease amount of tries for the given RADIUS server that - %% that will be used for next RADIUS requests towards this RADIUS server. - ets:update_counter(?MODULE, {ServerIP, Port}, -FailedTries) - end; - [] -> - ok - end, - case find_suitable_peer(UpstreamServers) of + maybe_failover(Request, Response, Options). + +maybe_failover(Request, Response, Options) -> + UpstreamServers = proplists:get_value(failover, Options, []), + case eradius_client_mngr:find_suitable_peer(UpstreamServers) of [] -> Response; {NewPeer, NewPool} -> @@ -329,221 +310,9 @@ update_client_response(bad_authenticator, MetricsInfo, _) -> eradius_counter:inc update_client_response(unknown_req_type, MetricsInfo, _) -> eradius_counter:inc_counter(unknownTypes, MetricsInfo); update_client_response(_, _, _) -> ok. -%% @private -reconfigure() -> - catch gen_server:call(?SERVER, reconfigure, ?RECONFIGURE_TIMEOUT). - -%% ------------------------------------------------------------------------------------------ -%% -- socket process manager --record(state, { - socket_ip :: null | inet:ip_address(), - no_ports = 1 :: pos_integer(), - idcounters = maps:new() :: map(), - sockets = array:new() :: array:array(), - sup :: pid(), - clients = [] :: [{{integer(),integer(),integer(),integer()}, integer()}] - }). - -%% @private -init([]) -> - {ok, Sup} = eradius_client_sup:start_link(), - case configure(#state{socket_ip = null, sup = Sup}) of - {error, Error} -> {stop, Error}; - Else -> Else - end. - -%% @private -handle_call({wanna_send, Peer = {_PeerName, PeerSocket}, _MetricsInfo}, _From, State) -> - {PortIdx, ReqId, NewIdCounters} = next_port_and_req_id(PeerSocket, State#state.no_ports, State#state.idcounters), - {SocketProcess, NewSockets} = find_socket_process(PortIdx, State#state.sockets, State#state.socket_ip, State#state.sup), - IsCreated = lists:member(Peer, State#state.clients), - NewState = case IsCreated of - false -> - State#state{idcounters = NewIdCounters, sockets = NewSockets, clients = [Peer | State#state.clients]}; - true -> - State#state{idcounters = NewIdCounters, sockets = NewSockets} - end, - {reply, {SocketProcess, ReqId}, NewState}; - -%% @private -handle_call(reconfigure, _From, State) -> - case configure(State) of - {error, Error} -> {reply, Error, State}; - {ok, NState} -> {reply, ok, NState} - end; - -%% @private -handle_call(_OtherCall, _From, State) -> - {noreply, State}. - -%% @private -handle_cast(_Msg, State) -> {noreply, State}. - -%% @private -handle_info({PortIdx, Pid}, State = #state{sockets = Sockets}) -> - NSockets = update_socket_process(PortIdx, Sockets, Pid), - {noreply, State#state{sockets = NSockets}}; - -handle_info(_Info, State) -> - {noreply, State}. - -%% @private -terminate(_Reason, _State) -> ok. - -%% @private -code_change(_OldVsn, State, _Extra) -> {ok, State}. - -%% @private -configure(State) -> - case ets:info(?MODULE) of - undefined -> - prepare_pools(); - _ -> - %% if ets table is already exists - which could be in a case of - %% reconfigure, just re-create the table and fill it with newly - %% configured pools of RADIUS upstream servers - ets:delete(?MODULE), - prepare_pools() - end, - {ok, ClientPortCount} = application:get_env(eradius, client_ports), - {ok, ClientIP} = application:get_env(eradius, client_ip), - case parse_ip(ClientIP) of - {ok, Address} -> - configure_address(State, ClientPortCount, Address); - {error, _} -> - ?LOG(error, "Invalid RADIUS client IP (parsing failed): ~p", [ClientIP]), - {error, {bad_client_ip, ClientIP}} - end. - --ifdef(TEST). - -get_state() -> - State = sys:get_state(?SERVER), - Keys = record_info(fields, state), - Values = tl(tuple_to_list(State)), - maps:from_list(lists:zip(Keys, Values)). - --endif. - -%% private -prepare_pools() -> - ets:new(?MODULE, [ordered_set, public, named_table, {keypos, 1}, {write_concurrency,true}]), - lists:foreach(fun({_PoolName, Servers}) -> prepare_pool(Servers) end, application:get_env(eradius, servers_pool, [])), - lists:foreach(fun(Server) -> store_upstream_servers(Server) end, application:get_env(eradius, servers, [])), - init_server_status_metrics(). - -prepare_pool([]) -> ok; -prepare_pool([{Addr, Port, _, Opts} | Servers]) -> - Retries = proplists:get_value(retries, Opts, ?DEFAULT_RETRIES), - store_radius_server_from_pool(Addr, Port, Retries), - prepare_pool(Servers); -prepare_pool([{Addr, Port, _} | Servers]) -> - store_radius_server_from_pool(Addr, Port, ?DEFAULT_RETRIES), - prepare_pool(Servers). - -store_upstream_servers({Server, _}) -> - store_upstream_servers(Server); -store_upstream_servers({Server, _, _}) -> - store_upstream_servers(Server); -store_upstream_servers(Server) -> - HandlerDefinitions = application:get_env(eradius, Server, []), - UpdatePoolFn = fun (HandlerOpts) -> - {DefaultRoute, Routes, Retries} = eradius_proxy:get_routes_info(HandlerOpts), - eradius_proxy:put_default_route_to_pool(DefaultRoute, Retries), - eradius_proxy:put_routes_to_pool(Routes, Retries) - end, - lists:foreach(fun (HandlerDefinition) -> - case HandlerDefinition of - {{_, []}, _} -> ok; - {{_, _, []}, _} -> ok; - {{_, HandlerOpts}, _} -> UpdatePoolFn(HandlerOpts); - {{_, _, HandlerOpts}, _} -> UpdatePoolFn(HandlerOpts); - _HandlerDefinition -> ok - end - end, - HandlerDefinitions). - -%% private -store_radius_server_from_pool(Addr, Port, Retries) when is_tuple(Addr) and is_integer(Port) and is_integer(Retries) -> - ets:insert(?MODULE, {{Addr, Port}, Retries, Retries}); -store_radius_server_from_pool(Addr, Port, Retries) when is_list(Addr) and is_integer(Port) and is_integer(Retries) -> - IP = get_ip(Addr), - ets:insert(?MODULE, {{IP, Port}, Retries, Retries}); -store_radius_server_from_pool(Addr, Port, Retries) -> - ?LOG(error, "bad RADIUS upstream server specified in RADIUS servers pool configuration ~p", [{Addr, Port, Retries}]), - error(badarg). - -configure_address(State = #state{socket_ip = OAdd, sockets = Sockts}, NPorts, NAdd) -> - case OAdd of - null -> - {ok, State#state{socket_ip = NAdd, no_ports = NPorts}}; - NAdd -> - configure_ports(State, NPorts); - _ -> - ?LOG(info, "Reopening RADIUS client sockets (client_ip changed to ~s)", [inet:ntoa(NAdd)]), - array:map( - fun(_PortIdx, undefined) -> - ok; - (_PortIdx, Socket) -> - eradius_client_socket:close(Socket) - end, Sockts), - {ok, State#state{sockets = array:new(), socket_ip = NAdd, no_ports = NPorts}} - end. - -configure_ports(State = #state{no_ports = OPorts, sockets = Sockets}, NPorts) -> - if - OPorts =< NPorts -> - {ok, State#state{no_ports = NPorts}}; - true -> - Counters = fix_counters(NPorts, State#state.idcounters), - NSockets = close_sockets(NPorts, Sockets), - {ok, State#state{sockets = NSockets, no_ports = NPorts, idcounters = Counters}} - end. - -fix_counters(NPorts, Counters) -> - maps:map(fun(_Peer, Value = {NextPortIdx, _NextReqId}) when NextPortIdx < NPorts -> Value; - (_Peer, {_NextPortIdx, NextReqId}) -> {0, NextReqId} - end, Counters). - -close_sockets(NPorts, Sockets) -> - case array:size(Sockets) =< NPorts of - true -> - Sockets; - false -> - List = array:to_list(Sockets), - {_, Rest} = lists:split(NPorts, List), - lists:map( - fun(undefined) -> ok; - (Socket) -> eradius_client_socket:close(Socket) - end, Rest), - array:resize(NPorts, Sockets) - end. - -next_port_and_req_id(Peer, NumberOfPorts, Counters) -> - case Counters of - #{Peer := {NextPortIdx, ReqId}} when ReqId < 255 -> - NextReqId = (ReqId + 1); - #{Peer := {PortIdx, 255}} -> - NextPortIdx = (PortIdx + 1) rem (NumberOfPorts - 1), - NextReqId = 0; - _ -> - NextPortIdx = erlang:phash2(Peer, NumberOfPorts), - NextReqId = 0 - end, - NewCounters = Counters#{Peer => {NextPortIdx, NextReqId}}, - {NextPortIdx, NextReqId, NewCounters}. - -find_socket_process(PortIdx, Sockets, SocketIP, Sup) -> - case array:get(PortIdx, Sockets) of - undefined -> - {ok, Socket} = eradius_client_socket:new(Sup, SocketIP), - {Socket, array:set(PortIdx, Socket, Sockets)}; - Socket -> - {Socket, Sockets} - end. - -update_socket_process(PortIdx, Sockets, Pid) -> - array:set(PortIdx, Pid, Sockets). +%%%========================================================================= +%%% internal functions +%%%========================================================================= parse_ip(undefined) -> {ok, undefined}; @@ -551,22 +320,9 @@ parse_ip(Address) when is_list(Address) -> inet_parse:address(Address); parse_ip(T = {_, _, _, _}) -> {ok, T}; -parse_ip(T = {_, _, _, _, _, _}) -> +parse_ip(T = {_, _, _, _, _, _, _, _}) -> {ok, T}. -init_server_status_metrics() -> - case application:get_env(eradius, server_status_metrics_enabled, false) of - false -> - ok; - true -> - %% That will be called at eradius startup and we must be sure that prometheus - %% application already started if server status metrics supposed to be used - application:ensure_all_started(prometheus), - ets:foldl(fun ({{Addr, Port}, _, _}, _Acc) -> - eradius_counter:set_boolean_metric(server_status, [Addr, Port], false) - end, [], ?MODULE) - end. - make_metrics_info(Options, {ServerIP, ServerPort}) -> ServerName = proplists:get_value(server_name, Options, undefined), ClientName = proplists:get_value(client_name, Options, undefined), @@ -650,28 +406,6 @@ client_response_counter_account_match_spec_compile() -> MatchSpecCompile end. -find_suitable_peer(undefined) -> - []; -find_suitable_peer([]) -> - []; -find_suitable_peer([{Host, Port, Secret} | Pool]) when is_list(Host) -> - try - IP = get_ip(Host), - find_suitable_peer([{IP, Port, Secret} | Pool]) - catch _:_ -> - %% can't resolve ip by some reasons, just ignore it - find_suitable_peer(Pool) - end; -find_suitable_peer([{IP, Port, Secret} | Pool]) -> - case ets:lookup(?MODULE, {IP, Port}) of - [] -> - find_suitable_peer(Pool); - [{{IP, Port}, _Retries, _InitialRetries}] -> - {{IP, Port, Secret}, Pool} - end; -find_suitable_peer([{IP, Port, Secret, _Opts} | Pool]) -> - find_suitable_peer([{IP, Port, Secret} | Pool]). - get_ip(Host) -> case inet:gethostbyname(Host) of {ok, #hostent{h_addrtype = inet, h_addr_list = [IP]}} -> diff --git a/src/eradius_client_mngr.erl b/src/eradius_client_mngr.erl new file mode 100644 index 00000000..3a162f66 --- /dev/null +++ b/src/eradius_client_mngr.erl @@ -0,0 +1,385 @@ +%% Copyright (c) 2002-2007, Martin Björklund and Torbjörn Törnkvist +%% Copyright (c) 2011, Travelping GmbH +%% +%% SPDX-License-Identifier: MIT +%% +-module(eradius_client_mngr). + +-behaviour(gen_server). + +%% external API +-export([start_link/0, wanna_send/1, wanna_send/2, reconfigure/0, reconfigure/1]). + +%% internal API +-export([store_radius_server_from_pool/3, + request_failed/3, + restore_upstream_server/1, + find_suitable_peer/1]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-ifdef(TEST). +-export([get_state/0, servers/0, servers/1, init_server_status_metrics/0]). +-endif. + +-include_lib("kernel/include/logger.hrl"). +-include_lib("kernel/include/inet.hrl"). +-include("eradius_internal.hrl"). + +-type client_opts() :: + #{family => inet | inet6, + ip => any | inet:ip_address(), + active_n => once | non_neg_integer(), + recbuf => non_neg_integer(), + sndbuf => non_neg_integer(), + server_pool => [term()], + servers => [term()]}. +-type client_config() :: + #{family := inet | inet6, + ip := any | inet:ip_address(), + active_n := once | non_neg_integer(), + recbuf := non_neg_integer(), + sndbuf := non_neg_integer(), + servers_pool => [term()], + servers => [term()]}. + +-export_type([client_config/0]). + +-record(state, { + config :: client_config(), + socket_id :: {Family :: inet | inet6, IP :: any | inet:ip_address()}, + no_ports = 1 :: pos_integer(), + idcounters = maps:new() :: map(), + sockets = array:new() :: array:array(), + clients = [] :: [{{integer(),integer(),integer(),integer()}, integer()}] + }). + +-define(SERVER, ?MODULE). + +-define(RECONFIGURE_TIMEOUT, 15000). + +%%%========================================================================= +%%% API +%%%========================================================================= + +start_link() -> + case client_config(default_client_opts()) of + {ok, Config} -> gen_server:start_link({local, ?SERVER}, ?MODULE, [Config], []); + {error, _} = Error -> Error + end. + +wanna_send(Peer) -> + gen_server:call(?SERVER, {wanna_send, Peer}). + +wanna_send(Node, Peer) -> + gen_server:call({?SERVER, Node}, {wanna_send, Peer}). + +%% @private +reconfigure() -> + case client_config(default_client_opts()) of + {ok, Config} -> reconfigure(Config); + {error, _} = Error -> Error + end. + +%% @doc reconfigure the Radius client +reconfigure(Config) -> + catch gen_server:call(?SERVER, {reconfigure, Config}, ?RECONFIGURE_TIMEOUT). + +request_failed(ServerIP, Port, Options) -> + case ets:lookup(?MODULE, {ServerIP, Port}) of + [{{ServerIP, Port}, Retries, InitialRetries}] -> + FailedTries = proplists:get_value(retries, Options, ?DEFAULT_RETRIES), + %% Mark the given RADIUS server as 'non-active' if there were more tries + %% than possible + if FailedTries >= Retries -> + ets:delete(?MODULE, {ServerIP, Port}), + Timeout = application:get_env(eradius, unreachable_timeout, 60), + timer:apply_after(Timeout * 1000, ?MODULE, restore_upstream_server, + [{ServerIP, Port, InitialRetries, InitialRetries}]); + true -> + %% RADIUS client tried to send a request to the {ServierIP, Port} RADIUS + %% server. There were done FailedTries tries and all of them failed. + %% So decrease amount of tries for the given RADIUS server that + %% that will be used for next RADIUS requests towards this RADIUS server. + ets:update_counter(?MODULE, {ServerIP, Port}, -FailedTries) + end; + [] -> + ok + end. + +restore_upstream_server({ServerIP, Port, Retries, InitialRetries}) -> + ets:insert(?MODULE, {{ServerIP, Port}, Retries, InitialRetries}). + +find_suitable_peer(undefined) -> + []; +find_suitable_peer([]) -> + []; +find_suitable_peer([{Host, Port, Secret} | Pool]) when is_list(Host) -> + try + IP = get_ip(Host), + find_suitable_peer([{IP, Port, Secret} | Pool]) + catch _:_ -> + %% can't resolve ip by some reasons, just ignore it + find_suitable_peer(Pool) + end; +find_suitable_peer([{IP, Port, Secret} | Pool]) -> + case ets:lookup(?MODULE, {IP, Port}) of + [] -> + find_suitable_peer(Pool); + [{{IP, Port}, _Retries, _InitialRetries}] -> + {{IP, Port, Secret}, Pool} + end; +find_suitable_peer([{IP, Port, Secret, _Opts} | Pool]) -> + find_suitable_peer([{IP, Port, Secret} | Pool]). + +-ifdef(TEST). + +get_state() -> + State = sys:get_state(?SERVER), + Keys = record_info(fields, state), + Values = tl(tuple_to_list(State)), + maps:from_list(lists:zip(Keys, Values)). + +servers() -> + ets:tab2list(?MODULE). + +servers(Key) -> + ets:lookup(?MODULE, Key). + +-endif. + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init([#{no_ports := NPorts} = Config]) -> + ets:new(?MODULE, [public, named_table, ordered_set, {keypos, 1}, {write_concurrency,true}]), + prepare_pools(Config), + + State = #state{ + config = Config, + socket_id = socket_id(Config), + no_ports = NPorts}, + {ok, State}. + +%% @private +handle_call({wanna_send, Peer = {_PeerName, PeerSocket}}, _From, + #state{config = Config, + no_ports = NoPorts, idcounters = IdCounters, + sockets = Sockets, clients = Clients} = State0) -> + {PortIdx, ReqId, NewIdCounters} = next_port_and_req_id(PeerSocket, NoPorts, IdCounters), + {SocketProcess, NewSockets} = find_socket_process(PortIdx, Sockets, Config), + State1 = State0#state{idcounters = NewIdCounters, sockets = NewSockets}, + State = + case lists:member(Peer, Clients) of + false -> State1#state{clients = [Peer | Clients]}; + true -> State1 + end, + {reply, {SocketProcess, ReqId}, State}; + +%% @private +handle_call({reconfigure, Config}, _From, State0) -> + ets:delete_all_objects(?MODULE), + prepare_pools(Config), + + State = reconfigure_address(Config, State0), + {reply, ok, State}; + +%% @private +handle_call(_OtherCall, _From, State) -> + {noreply, State}. + +%% @private +handle_cast(_Msg, State) -> {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +%% @private +terminate(_Reason, _State) -> ok. + +%% @private +code_change(_OldVsn, State, _Extra) -> {ok, State}. + +%%%========================================================================= +%%% internal functions +%%%========================================================================= + +socket_id(#{family := Family, ip := IP}) -> + {Family, IP}. + +get_ip(Host) -> + case inet:gethostbyname(Host) of + {ok, #hostent{h_addrtype = inet, h_addr_list = [IP]}} -> + IP; + {ok, #hostent{h_addrtype = inet, h_addr_list = [_ | _] = IPs}} -> + Index = rand:uniform(length(IPs)), + lists:nth(Index, IPs); + _ -> error(badarg) + end. + +%% @private +-spec default_client_opts() -> client_opts(). +default_client_opts() -> + #{ip => application:get_env(eradius, client_ip, any), + no_ports => application:get_env(eradius, client_ports, 10), + active_n => application:get_env(eradius, active_n, 100), + recbuf => application:get_env(eradius, recbuf, 8192), + sndbuf => application:get_env(eradius, sndbuf, 131072), + servers_pool => application:get_env(eradius, servers_pool, []), + servers => application:get_env(eradius, servers, []) + }. + + +-spec client_config(client_opts()) -> {ok, client_config()} | {error, _}. +client_config(#{ip := IP} = Opts) when is_atom(IP) -> + {ok, Opts#{family => inet6, ip := any}}; +client_config(#{ip := {_, _, _, _}} = Opts) -> + {ok, Opts#{family => inet}}; +client_config(#{ip := {_, _, _, _, _, _, _, _}} = Opts) -> + {ok, Opts#{family => inet6}}; +client_config(#{ip := Address} = Opts) when is_list(Address) -> + case inet_parse:address(Address) of + {ok, {_, _, _, _} = IP} -> + {ok, Opts#{family => inet, ip => IP}}; + {ok, {_, _, _, _, _, _, _, _} = IP} -> + {ok, Opts#{family => inet6, ip => IP}}; + _ -> + ?LOG(error, "Invalid RADIUS client IP (parsing failed): ~p", [Address]), + {error, {bad_client_ip, Address}} + end. + +%% private +prepare_pools(#{servers_pool := PoolList, servers := ServerList}) -> + lists:foreach(fun({_PoolName, Servers}) -> prepare_pool(Servers) end, PoolList), + lists:foreach(fun(Server) -> store_upstream_servers(Server) end, ServerList), + init_server_status_metrics(). + +prepare_pool([]) -> ok; +prepare_pool([{Addr, Port, _, Opts} | Servers]) -> + Retries = proplists:get_value(retries, Opts, ?DEFAULT_RETRIES), + store_radius_server_from_pool(Addr, Port, Retries), + prepare_pool(Servers); +prepare_pool([{Addr, Port, _} | Servers]) -> + store_radius_server_from_pool(Addr, Port, ?DEFAULT_RETRIES), + prepare_pool(Servers). + +store_upstream_servers({Server, _}) -> + store_upstream_servers(Server); +store_upstream_servers({Server, _, _}) -> + store_upstream_servers(Server); +store_upstream_servers(Server) -> + %% TBD: move proxy config into the proxy logic... + + HandlerDefinitions = application:get_env(eradius, Server, []), + UpdatePoolFn = fun (HandlerOpts) -> + {DefaultRoute, Routes, Retries} = eradius_proxy:get_routes_info(HandlerOpts), + eradius_proxy:put_default_route_to_pool(DefaultRoute, Retries), + eradius_proxy:put_routes_to_pool(Routes, Retries) + end, + lists:foreach(fun (HandlerDefinition) -> + case HandlerDefinition of + {{_, []}, _} -> ok; + {{_, _, []}, _} -> ok; + {{_, HandlerOpts}, _} -> UpdatePoolFn(HandlerOpts); + {{_, _, HandlerOpts}, _} -> UpdatePoolFn(HandlerOpts); + _HandlerDefinition -> ok + end + end, + HandlerDefinitions). + +%% private +store_radius_server_from_pool(Addr, Port, Retries) + when is_tuple(Addr), is_integer(Port), is_integer(Retries) -> + ets:insert(?MODULE, {{Addr, Port}, Retries, Retries}); +store_radius_server_from_pool(Addr, Port, Retries) + when is_list(Addr), is_integer(Port), is_integer(Retries) -> + IP = get_ip(Addr), + ets:insert(?MODULE, {{IP, Port}, Retries, Retries}); +store_radius_server_from_pool(Addr, Port, Retries) -> + ?LOG(error, "bad RADIUS upstream server specified in RADIUS servers pool configuration ~p", [{Addr, Port, Retries}]), + error(badarg). + +reconfigure_address(#{no_ports := NPorts} = Config, + #state{socket_id = OAdd, sockets = Sockts} = State) -> + NAdd = socket_id(Config), + case OAdd of + NAdd -> + reconfigure_ports(State, NPorts); + _ -> + ?LOG(info, "Reopening RADIUS client sockets (client_ip changed to ~s)", [inet:ntoa(NAdd)]), + array:map( + fun(_PortIdx, undefined) -> + ok; + (_PortIdx, Socket) -> + eradius_client_socket:close(Socket) + end, Sockts), + State#state{sockets = array:new(), socket_id = NAdd, no_ports = NPorts} + end. + +reconfigure_ports(State = #state{no_ports = OPorts, sockets = Sockets}, NPorts) -> + if + OPorts =< NPorts -> + State#state{no_ports = NPorts}; + true -> + Counters = fix_counters(NPorts, State#state.idcounters), + NSockets = close_sockets(NPorts, Sockets), + State#state{sockets = NSockets, no_ports = NPorts, idcounters = Counters} + end. + +fix_counters(NPorts, Counters) -> + maps:map(fun(_Peer, Value = {NextPortIdx, _NextReqId}) when NextPortIdx < NPorts -> Value; + (_Peer, {_NextPortIdx, NextReqId}) -> {0, NextReqId} + end, Counters). + +close_sockets(NPorts, Sockets) -> + case array:size(Sockets) =< NPorts of + true -> + Sockets; + false -> + List = array:to_list(Sockets), + {_, Rest} = lists:split(NPorts, List), + lists:map( + fun(undefined) -> ok; + (Socket) -> eradius_client_socket:close(Socket) + end, Rest), + array:resize(NPorts, Sockets) + end. + +next_port_and_req_id(Peer, NumberOfPorts, Counters) -> + case Counters of + #{Peer := {NextPortIdx, ReqId}} when ReqId < 255 -> + NextReqId = (ReqId + 1); + #{Peer := {PortIdx, 255}} -> + NextPortIdx = (PortIdx + 1) rem (NumberOfPorts - 1), + NextReqId = 0; + _ -> + NextPortIdx = erlang:phash2(Peer, NumberOfPorts), + NextReqId = 0 + end, + NewCounters = Counters#{Peer => {NextPortIdx, NextReqId}}, + {NextPortIdx, NextReqId, NewCounters}. + +find_socket_process(PortIdx, Sockets, Config) -> + case array:get(PortIdx, Sockets) of + undefined -> + {ok, Socket} = eradius_client_socket:new(Config), + {Socket, array:set(PortIdx, Socket, Sockets)}; + Socket -> + {Socket, Sockets} + end. + +%% @private +init_server_status_metrics() -> + case application:get_env(eradius, server_status_metrics_enabled, false) of + false -> + ok; + true -> + %% That will be called at eradius startup and we must be sure that prometheus + %% application already started if server status metrics supposed to be used + application:ensure_all_started(prometheus), + ets:foldl(fun ({{Addr, Port}, _, _}, _Acc) -> + eradius_counter:set_boolean_metric(server_status, [Addr, Port], false) + end, [], ?MODULE) + end. diff --git a/src/eradius_client_socket.erl b/src/eradius_client_socket.erl index 7472853e..22fddf3d 100644 --- a/src/eradius_client_socket.erl +++ b/src/eradius_client_socket.erl @@ -8,7 +8,7 @@ -behaviour(gen_server). %% API --export([new/2, start_link/1, send_request/5, close/1]). +-export([new/1, start_link/1, send_request/5, close/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -19,11 +19,11 @@ %%% API %%%========================================================================= -new(Sup, SocketIP) -> - eradius_client_sup:new(Sup, SocketIP). +new(Config) -> + eradius_client_socket_sup:new(Config). -start_link(SocketIP) -> - gen_server:start_link(?MODULE, [SocketIP], []). +start_link(Config) -> + gen_server:start_link(?MODULE, [Config], []). send_request(Socket, Peer, ReqId, Request, Timeout) -> try @@ -44,17 +44,17 @@ close(Socket) -> %%% gen_server callbacks %%%=================================================================== -init([SocketIP]) -> - case SocketIP of - undefined -> +init([#{family := Family, ip := IP, active_n := ActiveN, + recbuf := RecBuf, sndbuf := SndBuf} = _Config]) -> + case IP of + any -> ExtraOptions = []; - SocketIP when is_tuple(SocketIP) -> - ExtraOptions = [{ip, SocketIP}] + _ when is_tuple(IP) -> + ExtraOptions = [{ip, IP}] end, - ActiveN = application:get_env(eradius, active_n, 100), - RecBuf = application:get_env(eradius, recbuf, 8192), - SndBuf = application:get_env(eradius, sndbuf, 131072), - Opts = [{active, ActiveN}, binary, {recbuf, RecBuf}, {sndbuf, SndBuf} | ExtraOptions], + + Opts = [{active, ActiveN}, binary, {recbuf, RecBuf}, {sndbuf, SndBuf}, + Family | ExtraOptions], {ok, Socket} = gen_udp:open(0, Opts), State = #state{ diff --git a/src/eradius_client_socket_sup.erl b/src/eradius_client_socket_sup.erl new file mode 100644 index 00000000..954156be --- /dev/null +++ b/src/eradius_client_socket_sup.erl @@ -0,0 +1,52 @@ +-module(eradius_client_socket_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0, new/1]). + +%% Supervisor callbacks +-export([init/1]). + +-define(SERVER, ?MODULE). + +%%%=================================================================== +%%% API functions +%%%=================================================================== + +-spec start_link() -> {ok, Pid :: pid()} | + {error, {already_started, Pid :: pid()}} | + {error, {shutdown, term()}} | + {error, term()} | + ignore. +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +new(Config) -> + supervisor:start_child(?SERVER, [Config]). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== + +-spec init(Args :: term()) -> + {ok, {SupFlags :: supervisor:sup_flags(), + [ChildSpec :: supervisor:child_spec()]}} | + ignore. +init([]) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 5, + period => 10}, + + Child = #{id => eradius_client_socket, + start => {eradius_client_socket, start_link, []}, + restart => transient, + shutdown => 5000, + type => worker, + modules => [eradius_client_socket]}, + + {ok, {SupFlags, [Child]}}. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== diff --git a/src/eradius_client_sup.erl b/src/eradius_client_sup.erl index 7c40b2cd..6db9d435 100644 --- a/src/eradius_client_sup.erl +++ b/src/eradius_client_sup.erl @@ -3,7 +3,7 @@ -behaviour(supervisor). %% API --export([start_link/0, new/2]). +-export([start_link/1, new/2]). %% Supervisor callbacks -export([init/1]). @@ -14,16 +14,23 @@ %%% API functions %%%=================================================================== --spec start_link() -> {ok, Pid :: pid()} | +-spec start_link(Config :: eradius_client:client_config()) -> + {ok, Pid :: pid()} | {error, {already_started, Pid :: pid()}} | {error, {shutdown, term()}} | {error, term()} | ignore. -start_link() -> - supervisor:start_link(?MODULE, []). +start_link(Config) -> + supervisor:start_link(?MODULE, [Config]). -new(Sup, SocketId) -> - supervisor:start_child(Sup, [SocketId]). +new(Owner, SocketId) -> + Children = supervisor:which_children(Owner), + case lists:keyfind(eradius_client_socket_sup, 1, Children) of + {eradius_client_socket_sup, SupPid, _, _} when is_pid(SupPid) -> + supervisor:start_child(SupPid, [SocketId]); + _ -> + {error, dead} + end. %%%=================================================================== %%% Supervisor callbacks @@ -33,19 +40,26 @@ new(Sup, SocketId) -> {ok, {SupFlags :: supervisor:sup_flags(), [ChildSpec :: supervisor:child_spec()]}} | ignore. -init([]) -> - SupFlags = #{strategy => simple_one_for_one, +init([Opts]) -> + SupFlags = #{strategy => one_for_one, intensity => 5, period => 10}, + Client = + #{id => eradius_client, + start => {eradius_client, start_link, [self(), Opts]}, + restart => permanent, + shutdown => 5000, + type => worker, + modules => [eradius_client]}, + SocketSup = + #{id => eradius_client_socket_sup, + start => {eradius_client_socket_sup, start_link, []}, + restart => permanent, + shutdown => 5000, + type => supervisor, + modules => [eradius_client_socket_sup]}, - Child = #{id => eradius_client_socket, - start => {eradius_client_socket, start_link, []}, - restart => transient, - shutdown => 5000, - type => worker, - modules => [eradius_client_socket]}, - - {ok, {SupFlags, [Child]}}. + {ok, {SupFlags, [Client, SocketSup]}}. %%%=================================================================== %%% Internal functions diff --git a/src/eradius_internal.hrl b/src/eradius_internal.hrl new file mode 100644 index 00000000..cc814287 --- /dev/null +++ b/src/eradius_internal.hrl @@ -0,0 +1,2 @@ +-define(DEFAULT_RETRIES, 3). +-define(DEFAULT_TIMEOUT, 5000). diff --git a/src/eradius_proxy.erl b/src/eradius_proxy.erl index ff95eb89..5961b13b 100644 --- a/src/eradius_proxy.erl +++ b/src/eradius_proxy.erl @@ -290,9 +290,9 @@ get_routes_info(HandlerOpts) -> put_default_route_to_pool(false, _) -> ok; put_default_route_to_pool({default_route, {Host, Port, _Secret}}, Retries) -> - eradius_client:store_radius_server_from_pool(Host, Port, Retries); + eradius_client_mngr:store_radius_server_from_pool(Host, Port, Retries); put_default_route_to_pool({default_route, {Host, Port, _Secret}, _PoolName}, Retries) -> - eradius_client:store_radius_server_from_pool(Host, Port, Retries); + eradius_client_mngr:store_radius_server_from_pool(Host, Port, Retries); put_default_route_to_pool(_, _) -> ok. put_routes_to_pool(false, _Retries) -> ok; @@ -300,11 +300,11 @@ put_routes_to_pool({routes, Routes}, Retries) -> lists:foreach(fun (Route) -> case Route of {_RouteName, {Host, Port, _Secret}} -> - eradius_client:store_radius_server_from_pool(Host, Port, Retries); + eradius_client_mngr:store_radius_server_from_pool(Host, Port, Retries); {_RouteName, {Host, Port, _Secret}, _Pool} -> - eradius_client:store_radius_server_from_pool(Host, Port, Retries); + eradius_client_mngr:store_radius_server_from_pool(Host, Port, Retries); {Host, Port, _Secret, _Opts} -> - eradius_client:store_radius_server_from_pool(Host, Port, Retries); + eradius_client_mngr:store_radius_server_from_pool(Host, Port, Retries); _ -> ok end end, Routes). diff --git a/src/eradius_server.erl b/src/eradius_server.erl index 8e920b14..88198c1e 100644 --- a/src/eradius_server.erl +++ b/src/eradius_server.erl @@ -305,17 +305,21 @@ handle_request({HandlerMod, HandlerArg}, NasProp = #nas_prop{secret = Secret, na maps:from_list(eradius_log:collect_meta(Sender, Request))), eradius_log:write_request(Sender, Request), apply_handler_mod(HandlerMod, HandlerArg, Request, NasProp); - {bad_pdu, "Message-Authenticator Attribute is invalid" = Reason} -> - ?LOG(error, "~s INF: Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Reason]), + {bad_pdu, "Message-Authenticator Attribute is invalid"} -> + ?LOG(error, "~s INF: Message-Authenticator Attribute is invalid", + [printable_peer(ServerIP, Port)]), {discard, bad_authenticator}; - {bad_pdu, "Authenticator Attribute is invalid" = Reason} -> - ?LOG(error, "~s INF: Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Reason]), + {bad_pdu, "Authenticator Attribute is invalid"} -> + ?LOG(error, "~s INF: Authenticator Attribute is invalid", + [printable_peer(ServerIP, Port)]), {discard, bad_authenticator}; - {bad_pdu, "unknown request type" = Reason} -> - ?LOG(error, "~s INF: Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Reason]), + {bad_pdu, "unknown request type"} -> + ?LOG(error, "~s INF: unknown request type", + [printable_peer(ServerIP, Port)]), {discard, unknown_req_type}; {bad_pdu, Reason} -> - ?LOG(error, "~s INF: Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Reason]), + ?LOG(error, "~s INF: Could not decode the request, reason: ~s", + [printable_peer(ServerIP, Port), Reason]), {discard, malformed} end. diff --git a/src/eradius_sup.erl b/src/eradius_sup.erl index 342af84f..86093a41 100644 --- a/src/eradius_sup.erl +++ b/src/eradius_sup.erl @@ -25,6 +25,20 @@ init([]) -> NodeMon = {node_mon, {eradius_node_mon, start_link, []}, permanent, brutal_kill, worker, [eradius_node_mon]}, RadiusLog = {radius_log, {eradius_log, start_link, []}, permanent, brutal_kill, worker, [eradius_log]}, ServerTopSup = {server_top_sup, {eradius_server_top_sup, start_link, []}, permanent, infinity, supervisor, [eradius_server_top_sup]}, - Client = {client, {eradius_client, start_link, []}, permanent, 500, worker, [eradius_client]}, + ClientMngr = + #{id => client_mngr, + start => {eradius_client_mngr, start_link, []}, + restart => permanent, + shutdown => 500, + type => worker, + modules => [eradius_client_mngr]}, + ClientSocketSup = + #{id => eradius_client_socket_sup, + start => {eradius_client_socket_sup, start_link, []}, + restart => permanent, + shutdown => 5000, + type => supervisor, + modules => [eradius_client_socket_sup]}, - {ok, {SupFlags, [DictServer, NodeMon, StatsServer, StatsCollect, RadiusLog, ServerTopSup, Client]}}. + {ok, {SupFlags, [DictServer, NodeMon, StatsServer, StatsCollect, RadiusLog, + ServerTopSup, ClientSocketSup, ClientMngr]}}. diff --git a/test/eradius_client_SUITE.erl b/test/eradius_client_SUITE.erl index bdf972e6..b30d4e5f 100644 --- a/test/eradius_client_SUITE.erl +++ b/test/eradius_client_SUITE.erl @@ -100,8 +100,7 @@ end_per_testcase(_Test, Config) -> %% STUFF getSocketCount() -> - #{sup := Sup} = eradius_client:get_state(), - Counts = supervisor:count_children(Sup), + Counts = supervisor:count_children(eradius_client_socket_sup), proplists:get_value(active, Counts). testSocket(undefined) -> @@ -133,6 +132,8 @@ meckStop() -> parse_ip(undefined) -> {ok, undefined}; +parse_ip(any) -> + {ok, any}; parse_ip(Address) when is_list(Address) -> inet_parse:address(Address); parse_ip(T = {_, _, _, _}) -> @@ -148,9 +149,9 @@ test(false, Msg) -> false. check(OldState, NewState = #{no_ports := P}, null, A) -> check(OldState, NewState, P, A); -check(OldState, NewState = #{socket_ip := A}, P, null) -> check(OldState, NewState, P, A); -check(#{sockets := OS, no_ports := _OP, idcounters := _OC, socket_ip := OA}, - #{sockets := NS, no_ports := NP, idcounters := NC, socket_ip := NA}, +check(OldState, NewState = #{socket_id := {_, A}}, P, null) -> check(OldState, NewState, P, A); +check(#{sockets := OS, no_ports := _OP, idcounters := _OC, socket_id := {_, OA}}, + #{sockets := NS, no_ports := NP, idcounters := NC, socket_id := {_, NA}}, P, A) -> {ok, PA} = parse_ip(A), test(PA == NA, "Adress not configured") and @@ -181,9 +182,9 @@ send_request(_Config) -> send(FUN, Ports, Address) -> meckStart(), - OldState = eradius_client:get_state(), + OldState = eradius_client_mngr:get_state(), FUN(), - NewState = eradius_client:get_state(), + NewState = eradius_client_mngr:get_state(), true = check(OldState, NewState, Ports, Address), meckStop(). @@ -191,24 +192,23 @@ wanna_send(_Config) -> lists:map(fun(_) -> IP = {rand:uniform(100), rand:uniform(100), rand:uniform(100), rand:uniform(100)}, Port = rand:uniform(100), - MetricsInfo = {{undefined, undefined, undefined}, {undefined, undefined, undefined}}, - FUN = fun() -> gen_server:call(eradius_client, {wanna_send, {undefined, {IP, Port}}, MetricsInfo}) end, + FUN = fun() -> eradius_client_mngr:wanna_send({undefined, {IP, Port}}) end, send(FUN, null, null) end, lists:seq(1, 10)). %% socket shutdown is done asynchronous, the tests need to wait a bit for it to finish. reconf_address(_Config) -> - FUN = fun() -> eradius_client:reconfigure(), timer:sleep(100) end, + FUN = fun() -> eradius_client_mngr:reconfigure(), timer:sleep(100) end, application:set_env(eradius, client_ip, "7.13.23.42"), send(FUN, null, "7.13.23.42"). reconf_ports_30(_Config) -> - FUN = fun() -> gen_server:call(eradius_client, reconfigure), timer:sleep(100) end, + FUN = fun() -> eradius_client_mngr:reconfigure(), timer:sleep(100) end, application:set_env(eradius, client_ports, 30), send(FUN, 30, null). reconf_ports_10(_Config) -> - FUN = fun() -> gen_server:call(eradius_client, reconfigure), timer:sleep(100) end, + FUN = fun() -> eradius_client_mngr:reconfigure(), timer:sleep(100) end, application:set_env(eradius, client_ports, 10), send(FUN, 10, null). @@ -216,9 +216,9 @@ send_request_failover(_Config) -> ?equal(accept, eradius_test_handler:send_request_failover(?BAD_SERVER_IP)), {ok, Timeout} = application:get_env(eradius, unreachable_timeout), timer:sleep(Timeout * 1000), - ?equal([?BAD_SERVER_TUPLE], ets:lookup(eradius_client, ?BAD_SERVER_IP_ETS_KEY)), + ?equal([?BAD_SERVER_TUPLE], eradius_client_mngr:servers(?BAD_SERVER_IP_ETS_KEY)), ok. check_upstream_servers(_Config) -> - ?equal(?RADIUS_SERVERS, ets:tab2list(eradius_client)), + ?equal(?RADIUS_SERVERS, eradius_client_mngr:servers()), ok. diff --git a/test/eradius_metrics_SUITE.erl b/test/eradius_metrics_SUITE.erl index 60cc0fbb..d788d843 100644 --- a/test/eradius_metrics_SUITE.erl +++ b/test/eradius_metrics_SUITE.erl @@ -82,7 +82,7 @@ end_per_suite(_Config) -> ok. init_per_testcase(_, Config) -> - eradius_client:init_server_status_metrics(), + eradius_client_mngr:init_server_status_metrics(), Config. %% tests