5
5
-export ([start /3 ]).
6
6
-export ([init /1 , handle_call /3 , handle_cast /2 , handle_info /2 , terminate /2 , code_change /3 ]).
7
7
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" ).
9
11
10
12
start (SocketIP , Client , PortIdx ) ->
11
13
gen_server :start_link (? MODULE , [SocketIP , Client , PortIdx ], []).
12
14
13
15
init ([SocketIP , Client , PortIdx ]) ->
14
16
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 }.
25
39
26
40
handle_call (_Request , _From , State ) ->
27
41
{noreply , State }.
28
42
29
43
handle_cast (_Msg , State ) ->
30
44
{noreply , State }.
31
45
46
+ handle_info ({'$socket' , Socket , select , Info }, # state {socket = Socket } = State ) ->
47
+ handle_input (Socket , Info , State );
48
+
32
49
handle_info ({SenderPid , send_request , {IP , Port }, ReqId , EncRequest },
33
50
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
35
52
ok ->
36
53
ReqKey = {IP , Port , ReqId },
37
54
NPending = maps :put (ReqKey , SenderPid , Pending ),
@@ -41,31 +58,6 @@ handle_info({SenderPid, send_request, {IP, Port}, ReqId, EncRequest},
41
58
{noreply , State }
42
59
end ;
43
60
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
-
69
61
handle_info (close , State = # state {counter = Counter }) ->
70
62
case Counter of
71
63
0 -> {stop , normal , State };
@@ -80,3 +72,71 @@ terminate(_Reason, _State) ->
80
72
81
73
code_change (_OldVsn , State , _Extra ) ->
82
74
{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 .
0 commit comments