From f72680f0d9c8e3b94e666c3a89d4015723394887 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Tue, 28 Sep 2021 21:34:53 +0200 Subject: [PATCH 1/7] Adding support in eradius_server module for socket options --- src/eradius_server.erl | 19 ++++++++++++++----- src/eradius_server_sup.erl | 6 +++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/eradius_server.erl b/src/eradius_server.erl index b67ecfd1..7274416f 100644 --- a/src/eradius_server.erl +++ b/src/eradius_server.erl @@ -49,7 +49,7 @@ %% }). %% ''' -module(eradius_server). --export([start_link/3]). +-export([start_link/3, start_link/4]). -export_type([port_number/0, req_id/0]). %% internal @@ -92,11 +92,15 @@ -callback radius_request(#radius_request{}, #nas_prop{}, HandlerData :: term()) -> {reply, #radius_request{}} | noreply | {error, timeout}. -%% @private -spec start_link(atom(), inet:ip4_address(), port_number()) -> {ok, pid()} | {error, term()}. start_link(ServerName, IP = {A,B,C,D}, Port) -> Name = list_to_atom(lists:flatten(io_lib:format("eradius_server_~b.~b.~b.~b:~b", [A,B,C,D,Port]))), - gen_server:start_link({local, Name}, ?MODULE, {ServerName, IP, Port}, []). + gen_server:start_link({local, Name}, ?MODULE, {ServerName, IP, Port, []}, []). + +-spec start_link(atom(), inet:ip4_address(), port_number(), [inet:socket_setopt()]) -> {ok, pid()} | {error, term()}. +start_link(ServerName, IP = {A,B,C,D}, Port, Opts) -> + Name = list_to_atom(lists:flatten(io_lib:format("eradius_server_~b.~b.~b.~b:~b", [A,B,C,D,Port]))), + gen_server:start_link({local, Name}, ?MODULE, {ServerName, IP, Port, Opts}, []). stats(Server, Function) -> gen_server:call(Server, {stats, Function}). @@ -104,15 +108,20 @@ stats(Server, Function) -> %% ------------------------------------------------------------------------------------------ %% -- gen_server Callbacks %% @private -init({ServerName, IP, Port}) -> +init({ServerName, IP, Port, Opts}) -> process_flag(trap_exit, true), RecBuf = application:get_env(eradius, recbuf, 8192), case gen_udp:open(Port, [{active, once}, {ip, IP}, binary, {recbuf, RecBuf}]) of {ok, Socket} -> - {ok, #state{socket = Socket, + case inet:setopts(Socket, Opts) of + ok -> + {ok, #state{socket = Socket, ip = IP, port = Port, name = ServerName, transacts = ets:new(transacts, []), counter = eradius_counter:init_counter({IP, Port, ServerName})}}; + {error, Reason} -> + {stop, Reason} + end; {error, Reason} -> {stop, Reason} end. diff --git a/src/eradius_server_sup.erl b/src/eradius_server_sup.erl index 0a4a558b..65492e9a 100644 --- a/src/eradius_server_sup.erl +++ b/src/eradius_server_sup.erl @@ -18,7 +18,11 @@ start_link() -> start_instance(_ServerAddr = {ServerName, {IP, Port}}) -> ?LOG(info, "Starting RADIUS Listener at ~s", [printable_peer(IP, Port)]), - supervisor:start_child(?SERVER, [ServerName, IP, Port]). + supervisor:start_child(?SERVER, [ServerName, IP, Port]); + +start_instance(_ServerAddr = {ServerName, {IP, Port, Opts}}) -> + ?LOG(info, "Starting RADIUS Listener at ~s", [printable_peer(IP, Port)]), + supervisor:start_child(?SERVER, [ServerName, IP, Port, Opts]). stop_instance(_ServerAddr = {_ServerName, {IP, Port}}, Pid) -> ?LOG(info, "Stopping RADIUS Listener at ~s", [printable_peer(IP, Port)]), From f7b972ed3279f201659946ff2cd83f195642e178 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Fri, 1 Oct 2021 10:57:56 +0200 Subject: [PATCH 2/7] Commonize start_ling on eradius_server module --- src/eradius_config.erl | 34 +++++++++++++++++++++++++--------- src/eradius_server.erl | 21 +++++++-------------- src/eradius_server_mon.erl | 28 ++++++++++++++++++---------- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/eradius_config.erl b/src/eradius_config.erl index c3c99165..3fb9a5ae 100644 --- a/src/eradius_config.erl +++ b/src/eradius_config.erl @@ -30,17 +30,21 @@ validate_new_config_start(Servers, Nodes) -> map_helper(fun(Server) -> validate_new_server_config(Server, Nodes) end, Servers, flatten). validate_new_server_config({Name, {IP, ListOfPorts}}, Nodes) -> - validate_new_server_config(Name, get_app_env(Name), validate_ip(IP), validate_ports(ListOfPorts), Nodes). + validate_new_server_config(Name, get_app_env(Name), validate_ip(IP), validate_ports(ListOfPorts), [], Nodes); -validate_new_server_config(_Server, {invalid, _} = Invalid, _IP, _ListOfPorts, _Nodes) -> Invalid; -validate_new_server_config(_Server, _NasList, {invalid, _} = Invalid, _ListOfPorts, _Nodes) -> Invalid; -validate_new_server_config(_Server, _NasList, _IP, {invalid, _} = Invalid, _Nodes) -> Invalid; -validate_new_server_config(Server, NasList, IP, ListOfPorts, Nodes) -> +validate_new_server_config({Name, {IP, ListOfPorts, Opts}}, Nodes) -> + validate_new_server_config(Name, get_app_env(Name), validate_ip(IP), validate_ports(ListOfPorts), validate_options(Opts), Nodes). + +validate_new_server_config(_Server, {invalid, _} = Invalid, _IP, _ListOfPorts, _Opts, _Nodes) -> Invalid; +validate_new_server_config(_Server, _NasList, {invalid, _} = Invalid, _ListOfPorts, _Opts, _Nodes) -> Invalid; +validate_new_server_config(_Server, _NasList, _IP, {invalid, _} = Invalid, _Opts, _Nodes) -> Invalid; +validate_new_server_config(_Server, _NasList, _IP, _ListOfPorts, {invalid, _} = Invalid, _Nodes) -> Invalid; +validate_new_server_config(Server, NasList, IP, ListOfPorts, Opts, Nodes) -> case validate_new_nas_list(NasList, {IP, ListOfPorts, Nodes}) of {invalid, _} = Invalid -> Invalid; Values -> - lists:map(fun(Port) -> {Server, {IP, Port}, Values} end, ListOfPorts) + lists:map(fun(Port) -> {Server, {IP, Port, Opts}, Values} end, ListOfPorts) end. validate_new_nas_list(NasLists, ServerConfig) -> @@ -65,11 +69,11 @@ validate_behavior({Module, Nas, _Args} = Value) when is_atom(Module) andalso ?is false -> Value end; validate_behavior({Module, _, _}) when is_atom(Module) -> - ?invalid("bad NAS Id in Behavior specifification: ~p", [Module]); + ?invalid("bad NAS Id in Behavior specification: ~p", [Module]); validate_behavior({Module, _, _}) -> - ?invalid("bad module in Behavior specifification: ~p", [Module]); + ?invalid("bad module in Behavior specification: ~p", [Module]); validate_behavior(Term) -> - ?invalid("bad Term in Behavior specifification: ~p", [Term]). + ?invalid("bad Term in Behavior specification: ~p", [Term]). validate_arguments({Module, Nas, Args} = Value) -> case Module:validate_arguments(Args) of @@ -125,6 +129,11 @@ validate_port(Port) when ?pos_int(Port) -> Port; validate_port(Port) when is_integer(Port) -> ?invalid("port number out of range: ~p", [Port]); validate_port(Port) -> ?invalid("bad port number: ~p", [Port]). +validate_options(Opts) when is_list(Opts) -> + Opts; +validate_options(Opts) -> + ?invalid("expect a list of options: ~p", Opts). + check_root([First | _] = AllNodes) when is_tuple(First) -> map_helper(fun({Name, List}) -> case validate_handler_nodes(List) of @@ -228,6 +237,13 @@ validate_server(String) when is_list(String) -> _ -> {invalid, io_lib:format("bad address/port combination: ~p", [String])} end; +validate_server({IP, Port, Opts}) when is_list(Opts) -> + case validate_server({IP, Port}) of + {invalid, _Reason} = E -> + E; + {ValidIP, ValidPort} -> + {ValidIP, ValidPort, Opts} + end; validate_server(X) -> {invalid, io_lib:format("bad address/port combination: ~p", [X])}. diff --git a/src/eradius_server.erl b/src/eradius_server.erl index 7274416f..696a24cf 100644 --- a/src/eradius_server.erl +++ b/src/eradius_server.erl @@ -60,7 +60,6 @@ -behaviour(gen_server). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). --include_lib("stdlib/include/ms_transform.hrl"). -include_lib("kernel/include/logger.hrl"). -include("eradius_lib.hrl"). -include("dictionary.hrl"). @@ -93,9 +92,8 @@ {reply, #radius_request{}} | noreply | {error, timeout}. -spec start_link(atom(), inet:ip4_address(), port_number()) -> {ok, pid()} | {error, term()}. -start_link(ServerName, IP = {A,B,C,D}, Port) -> - Name = list_to_atom(lists:flatten(io_lib:format("eradius_server_~b.~b.~b.~b:~b", [A,B,C,D,Port]))), - gen_server:start_link({local, Name}, ?MODULE, {ServerName, IP, Port, []}, []). +start_link(ServerName, IP, Port) -> + start_link(ServerName, IP, Port, []). -spec start_link(atom(), inet:ip4_address(), port_number(), [inet:socket_setopt()]) -> {ok, pid()} | {error, term()}. start_link(ServerName, IP = {A,B,C,D}, Port, Opts) -> @@ -111,17 +109,12 @@ stats(Server, Function) -> init({ServerName, IP, Port, Opts}) -> process_flag(trap_exit, true), RecBuf = application:get_env(eradius, recbuf, 8192), - case gen_udp:open(Port, [{active, once}, {ip, IP}, binary, {recbuf, RecBuf}]) of + case gen_udp:open(Port, [{active, once}, {ip, IP}, binary, {recbuf, RecBuf}] ++ Opts) of {ok, Socket} -> - case inet:setopts(Socket, Opts) of - ok -> - {ok, #state{socket = Socket, - ip = IP, port = Port, name = ServerName, - transacts = ets:new(transacts, []), - counter = eradius_counter:init_counter({IP, Port, ServerName})}}; - {error, Reason} -> - {stop, Reason} - end; + {ok, #state{socket = Socket, + ip = IP, port = Port, name = ServerName, + transacts = ets:new(transacts, []), + counter = eradius_counter:init_counter({IP, Port, ServerName})}}; {error, Reason} -> {stop, Reason} end. diff --git a/src/eradius_server_mon.erl b/src/eradius_server_mon.erl index d54e6836..6f4b6f84 100644 --- a/src/eradius_server_mon.erl +++ b/src/eradius_server_mon.erl @@ -126,6 +126,8 @@ configure(#state{running = Running}) -> {ok, #state{running = NewRunning}} end. +server_naslist({ServerName, {IP, Port, _Opts}, HandlerList}) -> + server_naslist({ServerName, {IP, Port}, HandlerList}); server_naslist({ServerName, {IP, Port}, HandlerList}) -> lists:map(fun({NasId, NasIP, Secret, HandlerNodes, HandlerMod, HandlerArgs}) -> ServerInfo = eradius_lib:make_addr_info({ServerName, {IP, Port}}), @@ -144,18 +146,24 @@ update_server(Running, ToStop, ToStart) -> eradius_server_sup:stop_instance(ServerAddr, Pid), StoppedServer end, ToStop), - NewStarted = lists:map(fun(ServerAddr = {ServerName, Addr = {IP, Port}}) -> - case eradius_server_sup:start_instance(ServerAddr) of - {ok, Pid} -> - {ServerName, Addr, Pid}; - {error, Error} -> - ?LOG(error, "Could not start listener on host: ~s, occuring error: ~p", - [printable_peer(IP, Port), Error]) - end - end, ToStart), + StartFn = fun({ServerName, Addr = {IP, Port, _Opts}}=ServerAddr) -> + case eradius_server_sup:start_instance(ServerAddr) of + {ok, Pid} -> + {ServerName, Addr, Pid}; + {error, Error} -> + ?LOG(error, "Could not start listener on host: ~s, occurring error: ~p", + [printable_peer(IP, Port), Error]) + end + end, + NewStarted = lists:map(fun + ({ServerName, {IP, Port}}) -> + StartFn({ServerName, {IP, Port, []}}); + (ServerAddr) -> + StartFn(ServerAddr) + end, + ToStart), (Running -- Stopped) ++ NewStarted. update_nases(ToDelete, ToInsert) -> lists:foreach(fun(Nas) -> ets:delete_object(?NAS_TAB, Nas) end, ToDelete), lists:foreach(fun(Nas) -> ets:insert(?NAS_TAB, Nas) end, ToInsert). - From aa6bb606b8eee356f3766f769f3b8b0e58ca50e2 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Sun, 3 Oct 2021 12:43:36 +0200 Subject: [PATCH 3/7] Update src/eradius_server.erl Co-authored-by: Viacheslav Katsuba --- README.md | 58 ++++++++++++++++++++++------------- src/eradius_server.erl | 9 +++--- src/eradius_server_sup.erl | 5 ++- test/eradius_config_SUITE.erl | 25 +++++++++++++-- 4 files changed, 68 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e84e574d..a4aa0c89 100644 --- a/README.md +++ b/README.md @@ -13,20 +13,22 @@ several authentication mechanisms and dynamic configuration # Contents -* [Erlang Version Support](#erlang-version-support) -* [Building eradius](#building-eradius) -* [Using eradius](#using-eradius) -* [Run sample server](#run-sample-server) -* [Metrics](#metrics) -* [RADIUS server configuration](#radius-server-configuration) - * [eradius configuration example 1](#eradius-configuration-example-1) - * [eradius configuration example 2](#eradius-configuration-example-2) - * [eradius configuration example 3](#eradius-configuration-example-3) -* [Support of failover for client](#support-of-failover-for-client) - * [Failover configuration](#failover-configuration) - * [Failover Erlang code usage](#failover-erlang-code-usage) -* [Eradius counter aggregator](#eradius-counter-aggregator) -* [Tables](#tables) +- [eradius](#eradius) +- [Contents](#contents) +- [Erlang Version Support](#erlang-version-support) +- [Building eradius](#building-eradius) +- [Using eradius](#using-eradius) +- [Run sample server](#run-sample-server) +- [Metrics](#metrics) +- [RADIUS server configuration](#radius-server-configuration) + - [eradius configuration example 1](#eradius-configuration-example-1) + - [eradius configuration example 2](#eradius-configuration-example-2) + - [eradius configuration example 3](#eradius-configuration-example-3) +- [Support of failover for client](#support-of-failover-for-client) + - [Failover configuration](#failover-configuration) + - [Failover Erlang code usage](#failover-erlang-code-usage) +- [Eradius counter aggregator](#eradius-counter-aggregator) +- [Tables](#tables) # Erlang Version Support @@ -98,12 +100,13 @@ servers == { servers, [] } ``` Each server is tuple ({}): ``` -Server == { , { , [] } } | { , { , [] }, } +Server == { , { , [] } } | { , { , [], }, } ExtraServerOptions == [] +ExtraSocketOptions == [socket_setopt()] (see: https://erlang.org/doc/man/inet.html#setopts-2) ServerOption == {rate_config, | } ``` -Rate configuration can be configurated per server, in extra configuration, with a symbolic name or directly in server +Rate configuration can be configured per server, in extra configuration, with a symbolic name or directly in server ``` {SymbolicNameLimit, RateConfigList} RateConfigList == [] @@ -114,12 +117,15 @@ Each server is assigned a list of handlers. This list defines the NASes that are which handler is to process the request. Handler assignment: `{, []}` + ``` SymbolicName == Reference to a previously defined server. Handler == { , [] } ``` + If only one handler module is used, it can be defined globally as `{radius_callback, }`. If more than one handler modules are used, they have to be given in the HandlerDefinition: + ``` HandlerDefinition == {, , } | {, } HandlerMod == Handler module to process the received requests. @@ -140,8 +146,9 @@ Session nodes == {session_nodes, ['node@host', ...]} | {session_nodes, [{>, [{group, "NodeGroup1"}]} ] + [ {"10.18.14.2", <<"secret1">>, [{group, "NodeGroup1"}]} ] }, { {tposs_pcrf_handler2, "NAS2", [handler_arg3, handler_arg4]}, - [ {"10.18.14.3", <<"secret2">>, [{group, "NodeGroup2"}]} ] + [ {"10.18.14.3", <<"secret2">>, [{group, "NodeGroup2"}]} ] } ]} ]}] ``` + ## eradius configuration example 3 Example of full configuration with keys which can use in `eradius`: + ```erlang [{eradius, [ %% The IP address used to send RADIUS requests {client_ip, {127, 0, 0, 1}}, - %% The maximum number of open ports that will be used by RADIUS clients + %% The maximum number of open ports that will be used by RADIUS clients {client_ports, 256}, %% how long the binary response is kept before re-sending it {resend_timeout, 500}, @@ -234,7 +244,7 @@ Example of full configuration with keys which can use in `eradius`: ]}, {server_status_metrics_enabled, false}, {counter_aggregator, false}, - %% Size of RADIUS receive buffer + %% Size of RADIUS receive buffer {recbuf, 8192} ]}]. ``` @@ -251,6 +261,7 @@ Secondary RADIUS servers could be specified via RADIUS proxy configuration, with ## Failover configuration Configuration example of failover where the `pool_name` is `atom` specifies name of a pool of secondary RADIUS servers. + ```erlang [{eradius, [ %%% ... @@ -274,9 +285,11 @@ All pools are configured via: ## Failover Erlang code usage In a case when RADIUS proxy (eradius_proxy handler) is not used, a list of RADIUS upstream servers could be passed to the `eradius_client:send_radius_request/3` via options, for example: + ```erlang eradius_client:send_request(Server, Request, [{failover, [{"localhost", 1814, <<"secret">>}]}]). ``` + If `failover` option was not passed to the client through the options or RADIUS proxy configuration there should not be any performance impact as RADIUS client will try to a RADIUS request to only one RADIUS server that is defined in `eradius_client:send_request/3` options. For each secondary RADIUS server server status metrics could be enabled via boolean `server_status_metrics_enabled` configuration option. @@ -300,6 +313,7 @@ A list of RADIUS dictionaries to be loaded at startup. The atoms in this list ar the `priv` directory of the eradius application. Example: + ``` [dictionary, dictionary_cisco, dictionary_travelping] ``` diff --git a/src/eradius_server.erl b/src/eradius_server.erl index 696a24cf..e73be8f9 100644 --- a/src/eradius_server.erl +++ b/src/eradius_server.erl @@ -60,6 +60,7 @@ -behaviour(gen_server). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-include_lib("stdlib/include/ms_transform.hrl"). -include_lib("kernel/include/logger.hrl"). -include("eradius_lib.hrl"). -include("dictionary.hrl"). @@ -109,12 +110,12 @@ stats(Server, Function) -> init({ServerName, IP, Port, Opts}) -> process_flag(trap_exit, true), RecBuf = application:get_env(eradius, recbuf, 8192), - case gen_udp:open(Port, [{active, once}, {ip, IP}, binary, {recbuf, RecBuf}] ++ Opts) of + case gen_udp:open(Port, [{active, once}, {ip, IP}, binary, {recbuf, RecBuf} | Opts]) of {ok, Socket} -> {ok, #state{socket = Socket, - ip = IP, port = Port, name = ServerName, - transacts = ets:new(transacts, []), - counter = eradius_counter:init_counter({IP, Port, ServerName})}}; + ip = IP, port = Port, name = ServerName, + transacts = ets:new(transacts, []), + counter = eradius_counter:init_counter({IP, Port, ServerName})}}; {error, Reason} -> {stop, Reason} end. diff --git a/src/eradius_server_sup.erl b/src/eradius_server_sup.erl index 65492e9a..2387baf9 100644 --- a/src/eradius_server_sup.erl +++ b/src/eradius_server_sup.erl @@ -26,7 +26,10 @@ start_instance(_ServerAddr = {ServerName, {IP, Port, Opts}}) -> stop_instance(_ServerAddr = {_ServerName, {IP, Port}}, Pid) -> ?LOG(info, "Stopping RADIUS Listener at ~s", [printable_peer(IP, Port)]), - supervisor:terminate_child(?SERVER, Pid). + supervisor:terminate_child(?SERVER, Pid); + +stop_instance(ServerAddr = {_ServerName, {_IP, _Port, _Opts}}, Pid) -> + stop_instance(ServerAddr, Pid). all() -> lists:map(fun({_, Child, _, _}) -> Child end, supervisor:which_children(?SERVER)). diff --git a/test/eradius_config_SUITE.erl b/test/eradius_config_SUITE.erl index 59691523..76f855ba 100644 --- a/test/eradius_config_SUITE.erl +++ b/test/eradius_config_SUITE.erl @@ -23,10 +23,9 @@ -include("../include/eradius_lib.hrl"). -include("eradius_test.hrl"). -all() -> [config_1, config_2, config_nas_removing, config_with_ranges, log_test, generate_ip_list_test]. +all() -> [config_1, config_2, config_extra_options, config_nas_removing, config_with_ranges, log_test, generate_ip_list_test]. init_per_suite(Config) -> - % Is it a good practise? Copied fron client test {ok, _} = application:ensure_all_started(eradius), Config. @@ -118,6 +117,28 @@ config_2(_Config) -> }}, eradius_server_mon:lookup_handler(LocalHost, 1813, {10,18,14,2})), ok. +config_extra_options(_Config) -> + ExtraOptions = [{exit_on_close, true}, {linger, {true, 1}}], + Conf = [ + {root, [ + {{"root", []}, [{"10.18.14.3", <<"secret1">>}]} + ]}, + {servers, [ + {root, {"10.18.14.3", [1812, 1813], ExtraOptions}} + ]} + ], + apply_conf(Conf), + LocalHost = eradius_test_handler:localhost(tuple), + ?match({ok, {handler,[arg1,arg2]}, + #nas_prop{ + server_ip = LocalHost, + server_port = 1812, + nas_id = <<"NAS1_10.18.14.3">>, + nas_ip = {10,18,14,3}, + handler_nodes = ['node1@host1', 'node2@host2'] + }}, eradius_server_mon:lookup_handler(LocalHost, 1812, {10,18,14,3})), + ok. + config_nas_removing(_Config) -> Conf = [{servers, [ {root, {eradius_test_handler:localhost(ip), [1812, 1813]}} ]}, {root, [ ]}], From cd9dd09ca24b478b75239513ac6fcc9834224377 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Sun, 3 Oct 2021 16:19:18 +0200 Subject: [PATCH 4/7] fixing test --- test/eradius_config_SUITE.erl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/eradius_config_SUITE.erl b/test/eradius_config_SUITE.erl index 76f855ba..c34d75d5 100644 --- a/test/eradius_config_SUITE.erl +++ b/test/eradius_config_SUITE.erl @@ -119,17 +119,16 @@ config_2(_Config) -> config_extra_options(_Config) -> ExtraOptions = [{exit_on_close, true}, {linger, {true, 1}}], - Conf = [ - {root, [ - {{"root", []}, [{"10.18.14.3", <<"secret1">>}]} - ]}, - {servers, [ + Conf = [{servers, [ {root, {"10.18.14.3", [1812, 1813], ExtraOptions}} + ]}, + {root, [ + {{handler, "NAS", []}, [{"10.18.14.3", <<"secret1">>}]} ]} ], apply_conf(Conf), LocalHost = eradius_test_handler:localhost(tuple), - ?match({ok, {handler,[arg1,arg2]}, + ?match({ok, {handler, []}, #nas_prop{ server_ip = LocalHost, server_port = 1812, From 9f607ef5c3c01cae9021945bf540a4be32fa98c2 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Sun, 3 Oct 2021 16:22:02 +0200 Subject: [PATCH 5/7] Reverting readme contents formatting --- README.md | 30 ++++++++++++++---------------- test/eradius_config_SUITE.erl | 25 +++++++++++++++---------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a4aa0c89..4afce94c 100644 --- a/README.md +++ b/README.md @@ -13,22 +13,20 @@ several authentication mechanisms and dynamic configuration # Contents -- [eradius](#eradius) -- [Contents](#contents) -- [Erlang Version Support](#erlang-version-support) -- [Building eradius](#building-eradius) -- [Using eradius](#using-eradius) -- [Run sample server](#run-sample-server) -- [Metrics](#metrics) -- [RADIUS server configuration](#radius-server-configuration) - - [eradius configuration example 1](#eradius-configuration-example-1) - - [eradius configuration example 2](#eradius-configuration-example-2) - - [eradius configuration example 3](#eradius-configuration-example-3) -- [Support of failover for client](#support-of-failover-for-client) - - [Failover configuration](#failover-configuration) - - [Failover Erlang code usage](#failover-erlang-code-usage) -- [Eradius counter aggregator](#eradius-counter-aggregator) -- [Tables](#tables) +* [Erlang Version Support](#erlang-version-support) +* [Building eradius](#building-eradius) +* [Using eradius](#using-eradius) +* [Run sample server](#run-sample-server) +* [Metrics](#metrics) +* [RADIUS server configuration](#radius-server-configuration) + * [eradius configuration example 1](#eradius-configuration-example-1) + * [eradius configuration example 2](#eradius-configuration-example-2) + * [eradius configuration example 3](#eradius-configuration-example-3) +* [Support of failover for client](#support-of-failover-for-client) + * [Failover configuration](#failover-configuration) + * [Failover Erlang code usage](#failover-erlang-code-usage) +* [Eradius counter aggregator](#eradius-counter-aggregator) +* [Tables](#tables) # Erlang Version Support diff --git a/test/eradius_config_SUITE.erl b/test/eradius_config_SUITE.erl index c34d75d5..c8f559ef 100644 --- a/test/eradius_config_SUITE.erl +++ b/test/eradius_config_SUITE.erl @@ -119,23 +119,28 @@ config_2(_Config) -> config_extra_options(_Config) -> ExtraOptions = [{exit_on_close, true}, {linger, {true, 1}}], - Conf = [{servers, [ - {root, {"10.18.14.3", [1812, 1813], ExtraOptions}} - ]}, + Nodes = ['node1@host1', 'node2@host2'], + Conf = [{session_nodes, [ + {"NodeGroup", Nodes} + ] + }, + {servers, [ + {root, {eradius_test_handler:localhost(ip), [1812, 1813], ExtraOptions}} + ]}, {root, [ - {{handler, "NAS", []}, [{"10.18.14.3", <<"secret1">>}]} - ]} - ], + { {handler, "NAS", []}, + [ {"10.18.14.2", <<"secret2">>, [{group, "NodeGroup"}]} ]} + ]}], apply_conf(Conf), LocalHost = eradius_test_handler:localhost(tuple), ?match({ok, {handler, []}, #nas_prop{ server_ip = LocalHost, server_port = 1812, - nas_id = <<"NAS1_10.18.14.3">>, - nas_ip = {10,18,14,3}, - handler_nodes = ['node1@host1', 'node2@host2'] - }}, eradius_server_mon:lookup_handler(LocalHost, 1812, {10,18,14,3})), + nas_id = <<"NAS_10.18.14.2">>, + nas_ip = {10,18,14,2}, + handler_nodes = Nodes + }}, eradius_server_mon:lookup_handler(LocalHost, 1812, {10,18,14,2})), ok. config_nas_removing(_Config) -> From f6706b80ae020fe0faa73d3afd54bfbc385d1e9f Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Sun, 3 Oct 2021 16:43:47 +0200 Subject: [PATCH 6/7] Moving down the test --- test/eradius_config_SUITE.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/test/eradius_config_SUITE.erl b/test/eradius_config_SUITE.erl index c8f559ef..11925aa7 100644 --- a/test/eradius_config_SUITE.erl +++ b/test/eradius_config_SUITE.erl @@ -23,7 +23,7 @@ -include("../include/eradius_lib.hrl"). -include("eradius_test.hrl"). -all() -> [config_1, config_2, config_extra_options, config_nas_removing, config_with_ranges, log_test, generate_ip_list_test]. +all() -> [config_extra_options, config_1, config_2, config_nas_removing, config_with_ranges, log_test, generate_ip_list_test]. init_per_suite(Config) -> {ok, _} = application:ensure_all_started(eradius), @@ -118,26 +118,25 @@ config_2(_Config) -> ok. config_extra_options(_Config) -> - ExtraOptions = [{exit_on_close, true}, {linger, {true, 1}}], + ExtraOptions = [{linger, {true, 1}}], Nodes = ['node1@host1', 'node2@host2'], - Conf = [{session_nodes, [ - {"NodeGroup", Nodes} - ] - }, + Conf = [{session_nodes, Nodes}, {servers, [ {root, {eradius_test_handler:localhost(ip), [1812, 1813], ExtraOptions}} ]}, {root, [ - { {handler, "NAS", []}, - [ {"10.18.14.2", <<"secret2">>, [{group, "NodeGroup"}]} ]} + { {handler1, "NAS1", [arg1, arg2]}, + [{"10.18.14.2/30", <<"secret1">>}]}, + { {handler2, "NAS2", [arg1, arg2]}, + [{{10, 18, 14, 3}, <<"secret2">>, [{nas_id, <<"name">>}]}]} ]}], apply_conf(Conf), LocalHost = eradius_test_handler:localhost(tuple), - ?match({ok, {handler, []}, + ?match({ok, {handler1, [arg1, arg2]}, #nas_prop{ server_ip = LocalHost, server_port = 1812, - nas_id = <<"NAS_10.18.14.2">>, + nas_id = <<"NAS1_10.18.14.2">>, nas_ip = {10,18,14,2}, handler_nodes = Nodes }}, eradius_server_mon:lookup_handler(LocalHost, 1812, {10,18,14,2})), From 4098c654ef905b3c54b8e5ab91dbd6a48c477f96 Mon Sep 17 00:00:00 2001 From: Gian Lorenzo Meocci Date: Mon, 4 Oct 2021 15:47:27 +0200 Subject: [PATCH 7/7] Changing socket_opts format following the suggested comments --- README.md | 33 ++++++++++++++++++-- src/eradius_config.erl | 40 +++++++++++++++++++----- src/eradius_server.erl | 17 ++++++++-- test/eradius_config_SUITE.erl | 58 ++++++++++++++++++----------------- 4 files changed, 108 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 4afce94c..62dd3371 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ several authentication mechanisms and dynamic configuration * [eradius configuration example 1](#eradius-configuration-example-1) * [eradius configuration example 2](#eradius-configuration-example-2) * [eradius configuration example 3](#eradius-configuration-example-3) + * [eradius configuration example 3](#eradius-configuration-example-4) * [Support of failover for client](#support-of-failover-for-client) * [Failover configuration](#failover-configuration) * [Failover Erlang code usage](#failover-erlang-code-usage) @@ -98,9 +99,9 @@ servers == { servers, [] } ``` Each server is tuple ({}): ``` -Server == { , { , [] } } | { , { , [], }, } +Server == { , { , [] } } | { , { , [], } } ExtraServerOptions == [] -ExtraSocketOptions == [socket_setopt()] (see: https://erlang.org/doc/man/inet.html#setopts-2) +ExtraSocketOptions == [{socket_opts, [socket_setopt()]}] (see: https://erlang.org/doc/man/inet.html#setopts-2) ServerOption == {rate_config, | } ``` @@ -196,6 +197,34 @@ Different handlers are used for the sources. ## eradius configuration example 3 +Requests of different sources are forwarded to different nodes. +Different handlers are used for the sources. + +```erlang +[{eradius, [ + {session_nodes, [ + {"NodeGroup1", ['node1@host1', 'node2@host2']}, + {"NodeGroup2", ['node3@host3', 'node4@host4']} + ]}, + {servers, [ + {root, {"127.0.0.1", [1812, 1813], [{socket_opts, [{recbuf, 8192}, + {netns, "/var/run/netns/myns"}]}]}} + ]}, + {root, [ + { + {tposs_pcrf_handler1, "NAS1", [handler_arg1, handler_arg2]}, + [ {"10.18.14.2", <<"secret1">>, [{group, "NodeGroup1"}]} ] + }, + { + {tposs_pcrf_handler2, "NAS2", [handler_arg3, handler_arg4]}, + [ {"10.18.14.3", <<"secret2">>, [{group, "NodeGroup2"}]} ] + } + ]} +]}] +``` + +## eradius configuration example 4 + Example of full configuration with keys which can use in `eradius`: ```erlang diff --git a/src/eradius_config.erl b/src/eradius_config.erl index 3fb9a5ae..cd6b6930 100644 --- a/src/eradius_config.erl +++ b/src/eradius_config.erl @@ -3,7 +3,8 @@ -export([validate_new_config/0, validate_new_config/2, validate_config/1]). % Config validating API functions: -export([get_app_env/2, validate_ip/1, validate_port/1, validate_ports/1, - map_helper/3, map_helper/2, ok_error_helper/2, validate_secret/1]). + map_helper/3, map_helper/2, ok_error_helper/2, validate_secret/1, + validate_options/1, validate_socket_options/1, validate_server/1]). -export([generate_ip_list/2]). %% ------------------------------------------------------------------------------------------ @@ -130,10 +131,31 @@ validate_port(Port) when is_integer(Port) -> ?invalid("port number out of range: validate_port(Port) -> ?invalid("bad port number: ~p", [Port]). validate_options(Opts) when is_list(Opts) -> - Opts; + SocketOpts = proplists:get_value(socket_opts, Opts, []), + case validate_socket_options(SocketOpts) of + {invalid, Reason} = E -> + io:format("validate_socket_options: ~p", [Reason]), + E; + _ -> + Opts + end; validate_options(Opts) -> ?invalid("expect a list of options: ~p", Opts). +validate_socket_options(SocketOpts) when is_list(SocketOpts) -> + BannedOpts = [ip, binary, list, active], + IsBannedFn = fun(Opt) -> + proplists:is_defined(Opt, SocketOpts) + end, + case lists:any(IsBannedFn, BannedOpts) of + true -> + ?invalid("bad socket options specified: ~p", [SocketOpts]); + false -> + SocketOpts + end; +validate_socket_options(Opts) -> + ?invalid("expect a list of options: ~p", Opts). + check_root([First | _] = AllNodes) when is_tuple(First) -> map_helper(fun({Name, List}) -> case validate_handler_nodes(List) of @@ -194,11 +216,11 @@ validate_server_config([]) -> []; validate_server_config([{Server, NasList} | ConfigRest]) -> case validate_server(Server) of - E = {invalid, _} -> + {invalid, _} = E -> E; ValidServer -> case validate_nas_list(NasList) of - E = {invalid, _} -> + {invalid, _} = E -> E; ValidNasList -> case validate_server_config(ConfigRest) of @@ -238,11 +260,13 @@ validate_server(String) when is_list(String) -> {invalid, io_lib:format("bad address/port combination: ~p", [String])} end; validate_server({IP, Port, Opts}) when is_list(Opts) -> - case validate_server({IP, Port}) of - {invalid, _Reason} = E -> + case {validate_server({IP, Port}), validate_options(Opts)} of + {{invalid, _Reason} = E, _} -> + E; + {_, {invalid, _Reason} = E} -> E; - {ValidIP, ValidPort} -> - {ValidIP, ValidPort, Opts} + {{ValidIP, ValidPort}, ValidOpts} -> + {ValidIP, ValidPort, ValidOpts} end; validate_server(X) -> {invalid, io_lib:format("bad address/port combination: ~p", [X])}. diff --git a/src/eradius_server.erl b/src/eradius_server.erl index e73be8f9..855c833f 100644 --- a/src/eradius_server.erl +++ b/src/eradius_server.erl @@ -69,6 +69,7 @@ -define(RESEND_TIMEOUT, 5000). % how long the binary response is kept after sending it on the socket -define(RESEND_RETRIES, 3). % how often a reply may be resent -define(HANDLER_REPLY_TIMEOUT, 15000). % how long to wait before a remote handler is considered dead +-define(DEFAULT_RADIUS_SERVER_OPTS(IP), [{active, once}, {ip, IP}, binary]). -type port_number() :: 1..65535. -type req_id() :: byte(). @@ -109,8 +110,10 @@ stats(Server, Function) -> %% @private init({ServerName, IP, Port, Opts}) -> process_flag(trap_exit, true), - RecBuf = application:get_env(eradius, recbuf, 8192), - case gen_udp:open(Port, [{active, once}, {ip, IP}, binary, {recbuf, RecBuf} | Opts]) of + ExtraServerOptions = proplists:get_value(socket_opts, Opts, []), + DefaultRecBuf = application:get_env(eradius, recbuf, 8192), + ExtraServerOptionsWithBuf = add_recbuf_to_options(DefaultRecBuf, ExtraServerOptions), + case gen_udp:open(Port, ?DEFAULT_RADIUS_SERVER_OPTS(IP) ++ ExtraServerOptionsWithBuf) of {ok, Socket} -> {ok, #state{socket = Socket, ip = IP, port = Port, name = ServerName, @@ -166,6 +169,16 @@ handle_info({'EXIT', HandlerPid, _Reason}, State = #state{transacts = Transacts} handle_info(_Info, State) -> {noreply, State}. +%% @private +-spec add_recbuf_to_options(pos_integer(), proplists:proplist()) -> proplists:proplist(). +add_recbuf_to_options(RecBuf, Opts) -> + case proplists:get_value(recbuf, Opts) of + undefined -> + [{recbuf, RecBuf} | Opts]; + _Val -> + Opts + end. + %% @private terminate(_Reason, State) -> gen_udp:close(State#state.socket), diff --git a/test/eradius_config_SUITE.erl b/test/eradius_config_SUITE.erl index 11925aa7..a09756a2 100644 --- a/test/eradius_config_SUITE.erl +++ b/test/eradius_config_SUITE.erl @@ -23,7 +23,10 @@ -include("../include/eradius_lib.hrl"). -include("eradius_test.hrl"). -all() -> [config_extra_options, config_1, config_2, config_nas_removing, config_with_ranges, log_test, generate_ip_list_test]. +all() -> [config_1, config_2, config_options, + config_socket_options, config_nas_removing, + config_with_ranges, log_test, generate_ip_list_test, + test_validate_server]. init_per_suite(Config) -> {ok, _} = application:ensure_all_started(eradius), @@ -45,7 +48,7 @@ config_1(_Config) -> { {"NAS2", [arg1, arg2]}, [{{10, 18, 14, 3}, <<"secret2">>, [{nas_id, <<"name">>}]}]} ]}], - apply_conf(Conf), + ok = apply_conf(Conf), LocalHost = eradius_test_handler:localhost(tuple), ?match({ok, {?MODULE,[arg1,arg2]}, #nas_prop{ @@ -89,7 +92,7 @@ config_2(_Config) -> { {handler2, "NAS2", [arg3, arg4]}, [ {"10.18.14.2", <<"secret2">>, [{group, "NodeGroup2"}]} ] } ]}], - apply_conf(Conf), + ok = apply_conf(Conf), LocalHost = eradius_test_handler:localhost(tuple), ?match({ok, {handler1,[arg1,arg2]}, #nas_prop{ @@ -117,35 +120,34 @@ config_2(_Config) -> }}, eradius_server_mon:lookup_handler(LocalHost, 1813, {10,18,14,2})), ok. -config_extra_options(_Config) -> - ExtraOptions = [{linger, {true, 1}}], - Nodes = ['node1@host1', 'node2@host2'], - Conf = [{session_nodes, Nodes}, - {servers, [ - {root, {eradius_test_handler:localhost(ip), [1812, 1813], ExtraOptions}} - ]}, - {root, [ - { {handler1, "NAS1", [arg1, arg2]}, - [{"10.18.14.2/30", <<"secret1">>}]}, - { {handler2, "NAS2", [arg1, arg2]}, - [{{10, 18, 14, 3}, <<"secret2">>, [{nas_id, <<"name">>}]}]} - ]}], - apply_conf(Conf), - LocalHost = eradius_test_handler:localhost(tuple), - ?match({ok, {handler1, [arg1, arg2]}, - #nas_prop{ - server_ip = LocalHost, - server_port = 1812, - nas_id = <<"NAS1_10.18.14.2">>, - nas_ip = {10,18,14,2}, - handler_nodes = Nodes - }}, eradius_server_mon:lookup_handler(LocalHost, 1812, {10,18,14,2})), +config_socket_options(_Config) -> + Opts = [{netns, "/var/run/netns/net1"}], + ?match(Opts, eradius_config:validate_socket_options(Opts)), + Invalid = [{buffer, 512}, {active, false}], + ?match({invalid, _}, eradius_config:validate_socket_options(Invalid)), + Invalid2 = [{buffer, 512}, {ip, {127, 0, 0, 10}}], + ?match({invalid, _}, eradius_config:validate_socket_options(Invalid2)), + ok. + +config_options(_Config) -> + Opts = [{socket_opts, [{recbuf, 8192}, + {netns, "/var/run/netns/net1"}]}], + ?match(Opts, eradius_config:validate_options(Opts)), + ok. +test_validate_server(_Config) -> + SocketOpts = [{socket_opts, [{recbuf, 8192}, {netns, "/var/run/netns/net1"}]}], + Opts = {{127, 0, 0, 1}, 1812, SocketOpts}, + ?match(Opts, eradius_config:validate_server(Opts)), + Opts2 = {{127, 0, 0, 1}, "1812", SocketOpts}, + ?match({{127, 0, 0, 1}, 1812, SocketOpts}, eradius_config:validate_server(Opts2)), + Opts3 = {{127, 0, 0, 1}, 1812}, + ?match(Opts3, eradius_config:validate_server(Opts3)), ok. config_nas_removing(_Config) -> Conf = [{servers, [ {root, {eradius_test_handler:localhost(ip), [1812, 1813]}} ]}, {root, [ ]}], - apply_conf(Conf), + ok = apply_conf(Conf), ?match([], ets:tab2list(eradius_nas_tab)), ok. @@ -162,7 +164,7 @@ config_with_ranges(_Config) -> { {handler, "NAS", []}, [ {"10.18.14.2/30", <<"secret2">>, [{group, "NodeGroup"}]} ] } ]}], - apply_conf(Conf), + ok = apply_conf(Conf), LocalHost = eradius_test_handler:localhost(tuple), ?match({ok, {handler,[]}, #nas_prop{