Skip to content

Commit 53de024

Browse files
committed
WIP: experimental socket.erl code and hacks
1 parent 5fc23c6 commit 53de024

File tree

4 files changed

+145
-76
lines changed

4 files changed

+145
-76
lines changed

src/eradius_client.erl

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -162,28 +162,30 @@ restore_upstream_server({ServerIP, Port, Retries, InitialRetries}) ->
162162
proceed_response(Request, {ok, Response, Secret, Authenticator}, _Peer = {_ServerName, {ServerIP, Port}}, TS1, MetricsInfo, Options) ->
163163
update_client_request(Request#radius_request.cmd, MetricsInfo, erlang:monotonic_time() - TS1, Request),
164164
update_client_responses(MetricsInfo),
165-
case eradius_lib:decode_request(Response, Secret, Authenticator) of
166-
{bad_pdu, "Message-Authenticator Attribute is invalid" = Reason} ->
167-
update_client_response(bad_authenticator, MetricsInfo, Request),
168-
?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
169-
noreply;
170-
{bad_pdu, "Authenticator Attribute is invalid" = Reason} ->
171-
update_client_response(bad_authenticator, MetricsInfo, Request),
172-
?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
173-
noreply;
174-
{bad_pdu, "unknown request type" = Reason} ->
175-
update_client_response(unknown_req_type, MetricsInfo, Request),
176-
?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
177-
noreply;
178-
{bad_pdu, Reason} ->
179-
update_client_response(dropped, MetricsInfo, Request),
180-
?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
181-
maybe_failover(Request, noreply, {ServerIP, Port}, Options);
182-
Decoded ->
183-
update_server_status_metric(ServerIP, Port, true, Options),
184-
update_client_response(Decoded#radius_request.cmd, MetricsInfo, Request),
185-
{ok, Response, Authenticator}
186-
end;
165+
{ok, Response, Authenticator};
166+
167+
%% case eradius_lib:decode_request(Response, Secret, Authenticator) of
168+
%% {bad_pdu, "Message-Authenticator Attribute is invalid" = Reason} ->
169+
%% update_client_response(bad_authenticator, MetricsInfo, Request),
170+
%% ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
171+
%% noreply;
172+
%% {bad_pdu, "Authenticator Attribute is invalid" = Reason} ->
173+
%% update_client_response(bad_authenticator, MetricsInfo, Request),
174+
%% ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
175+
%% noreply;
176+
%% {bad_pdu, "unknown request type" = Reason} ->
177+
%% update_client_response(unknown_req_type, MetricsInfo, Request),
178+
%% ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
179+
%% noreply;
180+
%% {bad_pdu, Reason} ->
181+
%% update_client_response(dropped, MetricsInfo, Request),
182+
%% ?LOG(error, "~s INF: Noreply for request ~p. Could not decode the request, reason: ~s", [printable_peer(ServerIP, Port), Request, Reason]),
183+
%% maybe_failover(Request, noreply, {ServerIP, Port}, Options);
184+
%% Decoded ->
185+
%% update_server_status_metric(ServerIP, Port, true, Options),
186+
%% update_client_response(Decoded#radius_request.cmd, MetricsInfo, Request),
187+
%% {ok, Response, Authenticator}
188+
%% end;
187189

188190
proceed_response(Request, Response, {_ServerName, {ServerIP, Port}}, TS1, MetricsInfo, Options) ->
189191
update_client_responses(MetricsInfo),
@@ -404,7 +406,7 @@ configure(State) ->
404406

405407
%% private
406408
prepare_pools() ->
407-
ets:new(?MODULE, [ordered_set, public, named_table, {keypos, 1}, {write_concurrency,true}]),
409+
ets:new(?MODULE, [ordered_set, public, named_table, {keypos, 1}, {read_concurrency,true}]),
408410
lists:foreach(fun({_PoolName, Servers}) -> prepare_pool(Servers) end, application:get_env(eradius, servers_pool, [])),
409411
lists:foreach(fun(Server) -> store_upstream_servers(Server) end, application:get_env(eradius, servers, [])),
410412
init_server_status_metrics().
@@ -510,7 +512,9 @@ next_port_and_req_id(Peer, NumberOfPorts, Counters) ->
510512
NextReqId = 0
511513
end,
512514
NewCounters = Counters#{Peer => {NextPortIdx, NextReqId}},
513-
{NextPortIdx, NextReqId, NewCounters}.
515+
R = {NextPortIdx, NextReqId, NewCounters},
516+
%% ?LOG(info, "~s: ~p", [?FUNCTION_NAME, R]),
517+
R.
514518

515519
find_socket_process(PortIdx, Sockets, SocketIP, Sup) ->
516520
case array:get(PortIdx, Sockets) of

src/eradius_client_socket.erl

Lines changed: 97 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,50 @@
55
-export([start/3]).
66
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
77

8-
-record(state, {client, socket, pending, mode, counter}).
8+
-record(state, {client, socket, pending, mode, active_n, counter}).
9+
10+
-include_lib("kernel/include/logger.hrl").
911

1012
start(SocketIP, Client, PortIdx) ->
1113
gen_server:start_link(?MODULE, [SocketIP, Client, PortIdx], []).
1214

1315
init([SocketIP, Client, PortIdx]) ->
1416
Client ! {PortIdx, self()},
15-
case SocketIP of
16-
undefined ->
17-
ExtraOptions = [];
18-
SocketIP when is_tuple(SocketIP) ->
19-
ExtraOptions = [{ip, SocketIP}]
20-
end,
21-
RecBuf = application:get_env(eradius, recbuf, 8192),
22-
SndBuf = application:get_env(eradius, sndbuf, 131072),
23-
{ok, Socket} = gen_udp:open(0, [{active, once}, binary , {recbuf, RecBuf}, {sndbuf, SndBuf} | ExtraOptions]),
24-
{ok, #state{client = Client, socket = Socket, pending = maps:new(), mode = active, counter = 0}}.
17+
RecBuf = application:get_env(eradius, recbuf, 256*1024),
18+
SndBuf = application:get_env(eradius, sndbuf, 256*1024),
19+
20+
SockAddr =
21+
case SocketIP of
22+
undefined -> any;
23+
_ when is_tuple(SocketIP) -> SocketIP
24+
end,
25+
{ok, Socket} = socket:open(inet, dgram, udp),
26+
ok = socket:bind(Socket, #{family => inet, port => 0, addr => SockAddr}),
27+
ok = socket:setopt(Socket, socket, rcvbuf, RecBuf),
28+
ok = socket:setopt(Socket, socket, sndbuf, SndBuf),
29+
30+
self() ! {'$socket', Socket, select, undefined},
31+
32+
State = #state{client = Client,
33+
socket = Socket,
34+
pending = maps:new(),
35+
mode = active,
36+
active_n = 100,
37+
counter = 0},
38+
{ok, State}.
2539

2640
handle_call(_Request, _From, State) ->
2741
{noreply, State}.
2842

2943
handle_cast(_Msg, State) ->
3044
{noreply, State}.
3145

46+
handle_info({'$socket', Socket, select, Info}, #state{socket = Socket} = State) ->
47+
handle_input(Socket, Info, State);
48+
3249
handle_info({SenderPid, send_request, {IP, Port}, ReqId, EncRequest},
3350
State = #state{socket = Socket, pending = Pending, counter = Counter}) ->
34-
case gen_udp:send(Socket, IP, Port, EncRequest) of
51+
case socket:sendto(Socket, EncRequest, #{family => inet, port => Port, addr => IP}) of
3552
ok ->
3653
ReqKey = {IP, Port, ReqId},
3754
NPending = maps:put(ReqKey, SenderPid, Pending),
@@ -41,31 +58,6 @@ handle_info({SenderPid, send_request, {IP, Port}, ReqId, EncRequest},
4158
{noreply, State}
4259
end;
4360

44-
handle_info({udp, Socket, FromIP, FromPort, EncRequest},
45-
State = #state{socket = Socket, pending = Pending, mode = Mode, counter = Counter}) ->
46-
case eradius_lib:decode_request_id(EncRequest) of
47-
{ReqId, EncRequest} ->
48-
case maps:find({FromIP, FromPort, ReqId}, Pending) of
49-
error ->
50-
%% discard reply because we didn't expect it
51-
inet:setopts(Socket, [{active, once}]),
52-
{noreply, State};
53-
{ok, WaitingSender} ->
54-
WaitingSender ! {self(), response, ReqId, EncRequest},
55-
inet:setopts(Socket, [{active, once}]),
56-
NPending = maps:remove({FromIP, FromPort, ReqId}, Pending),
57-
NState = State#state{pending = NPending, counter = Counter-1},
58-
case {Mode, Counter-1} of
59-
{inactive, 0} -> {stop, normal, NState};
60-
_ -> {noreply, NState}
61-
end
62-
end;
63-
{bad_pdu, _} ->
64-
%% discard reply because it was malformed
65-
inet:setopts(Socket, [{active, once}]),
66-
{noreply, State}
67-
end;
68-
6961
handle_info(close, State = #state{counter = Counter}) ->
7062
case Counter of
7163
0 -> {stop, normal, State};
@@ -80,3 +72,71 @@ terminate(_Reason, _State) ->
8072

8173
code_change(_OldVsn, State, _Extra) ->
8274
{ok, State}.
75+
76+
%%%===================================================================
77+
%%% Internal functions
78+
%%%===================================================================
79+
80+
handle_input(Socket, Info, #state{active_n = ActiveN} = State) ->
81+
handle_input(Socket, Info, 0, ActiveN, State).
82+
83+
handle_input(_Socket, _Info, _Cnt, _Max, #state{mode = inactive, counter = 0} = State) ->
84+
{stop, normal, State};
85+
handle_input(Socket, _Info, Cnt, Max, State0)
86+
when Cnt >= Max ->
87+
%% break the loop and restart
88+
self() ! {'$socket', Socket, select, undefined},
89+
{noreply, State0};
90+
91+
handle_input(Socket, Info, Cnt, Max, State0) ->
92+
case socket:recvfrom(Socket, 0, [], nowait) of
93+
{error, _} ->
94+
State = handle_err_input(Socket, State0),
95+
handle_input(Socket, Info, Cnt + 1, Max, State);
96+
97+
{ok, {#{addr := IP, port := Port}, Data}} ->
98+
ArrivalTS = erlang:monotonic_time(),
99+
State = handle_message(ArrivalTS, IP, Port, Data, State0),
100+
handle_input(Socket, Info, Cnt + 1, Max, State);
101+
102+
{select, _SelectInfo} when Cnt == 0 ->
103+
%% there must be something in the error queue
104+
State = handle_err_input(Socket, State0),
105+
handle_input(Socket, Info, Cnt + 1, Max, State);
106+
107+
{select, _SelectInfo} ->
108+
{noreply, State0}
109+
end.
110+
111+
handle_err_input(Socket, State) ->
112+
case socket:recvmsg(Socket, [errqueue], 0) of
113+
{ok, #{addr := #{addr := IP, port := Port}, ctrl := Ctrl}} ->
114+
%% lists:foreach(handle_socket_error(_, IP, Port, State), Ctrl),
115+
ok;
116+
{error, timeout} ->
117+
ok;
118+
{error, ewouldblock} ->
119+
ok;
120+
121+
Other ->
122+
?LOG(error, "got unhandled error input: ~p", [Other])
123+
end,
124+
State.
125+
126+
handle_message(ArrivalTS, FromIP, FromPort, EncRequest,
127+
#state{pending = Pending, mode = Mode, counter = Counter} = State) ->
128+
case eradius_lib:decode_request_id(EncRequest) of
129+
{ReqId, EncRequest} ->
130+
case maps:find({FromIP, FromPort, ReqId}, Pending) of
131+
error ->
132+
%% discard reply because we didn't expect it
133+
State;
134+
{ok, WaitingSender} ->
135+
WaitingSender ! {self(), response, ReqId, EncRequest},
136+
NPending = maps:remove({FromIP, FromPort, ReqId}, Pending),
137+
State#state{pending = NPending, counter = Counter - 1}
138+
end;
139+
{bad_pdu, _} ->
140+
%% discard reply because it was malformed
141+
State
142+
end.

src/eradius_counter.erl

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,26 +86,26 @@ aggregate({Servers, {ResetTS, Nass}}) ->
8686
%% @doc Set Value for the given prometheus boolean metric by the given Name with
8787
%% the given values
8888
set_boolean_metric(Name, Labels, Value) ->
89-
case code:is_loaded(prometheus) of
90-
{file, _} ->
89+
%% case code:is_loaded(prometheus) of
90+
%% {file, _} ->
9191
try
9292
prometheus_boolean:set(Name, Labels, Value)
9393
catch _:_ ->
9494
prometheus_boolean:declare([{name, server_status}, {labels, [server_ip, server_port]},
9595
{help, "Status of an upstream RADIUS Server"}]),
9696
prometheus_boolean:set(Name, Labels, Value)
97-
end;
98-
_ ->
99-
ok
97+
%% end;
98+
%% _ ->
99+
%% ok
100100
end.
101101

102102
%% @doc Update the given histogram metric value
103103
%% NOTE: We use prometheus_histogram collector here instead of eradius_counter ets table because
104104
%% it is much easy to use histograms in this way. As we don't need to manage buckets and do
105105
%% the other histogram things in eradius, but prometheus.erl will do it for us
106106
observe(Name, {{ClientName, ClientIP, _}, {ServerName, ServerIP, ServerPort}} = MetricsInfo, Value, Help) ->
107-
case code:is_loaded(prometheus) of
108-
{file, _} ->
107+
%% case code:is_loaded(prometheus) of
108+
%% {file, _} ->
109109
try
110110
prometheus_histogram:observe(Name, [ServerIP, ServerPort, ServerName, ClientName, ClientIP], Value)
111111
catch _:_ ->
@@ -114,13 +114,14 @@ observe(Name, {{ClientName, ClientIP, _}, {ServerName, ServerIP, ServerPort}} =
114114
{duration_unit, milliseconds},
115115
{buckets, Buckets}, {help, Help}]),
116116
observe(Name, MetricsInfo, Value, Help)
117-
end;
118-
_ ->
119-
ok
117+
%% end;
118+
%% _ ->
119+
%% ok
120120
end.
121+
121122
observe(Name, #nas_prop{server_ip = ServerIP, server_port = ServerPort, nas_ip = NasIP, nas_id = NasId} = Nas, Value, ServerName, Help) ->
122-
case code:is_loaded(prometheus) of
123-
{file, _} ->
123+
%% case code:is_loaded(prometheus) of
124+
%% {file, _} ->
124125
try
125126
prometheus_histogram:observe(Name, [inet:ntoa(ServerIP), ServerPort, ServerName, inet:ntoa(NasIP), NasId], Value)
126127
catch _:_ ->
@@ -129,9 +130,9 @@ observe(Name, #nas_prop{server_ip = ServerIP, server_port = ServerPort, nas_ip =
129130
{duration_unit, milliseconds},
130131
{buckets, Buckets}, {help, Help}]),
131132
observe(Name, Nas, Value, ServerName, Help)
132-
end;
133-
_ ->
134-
ok
133+
%% end;
134+
%% _ ->
135+
%% ok
135136
end.
136137

137138
%% helper to be called from the aggregator to fetch this nodes values

src/eradius_lib.erl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
-module(eradius_lib).
2+
-compile([export_all, nowarn_export_all]).
3+
24
-export([del_attr/2, get_attr/2, encode_request/1, encode_reply/1, decode_request/2, decode_request/3, decode_request_id/1]).
35
-export([random_authenticator/0, zero_authenticator/0, pad_to/2, set_attr/3, get_attributes/1, set_attributes/2]).
46
-export([timestamp/0, timestamp/1, printable_peer/2, make_addr_info/1]).
@@ -133,6 +135,8 @@ encode_eap_message(#radius_request{eap_msg = <<>>}, EncReq) ->
133135
EncReq.
134136

135137
-spec encode_attributes(#radius_request{}, attribute_list()) -> {binary(), non_neg_integer()}.
138+
encode_attributes(_Req, Attributes) when is_binary(Attributes) ->
139+
{Attributes, byte_size(Attributes)};
136140
encode_attributes(Req, Attributes) ->
137141
F = fun ({A = #attribute{}, Val}, {Body, BodySize}) ->
138142
EncAttr = encode_attribute(Req, A, Val),

0 commit comments

Comments
 (0)