1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%-----------------------------------------------------------------
23%% Purpose: Interface to the UDP transport module for Megaco/H.248
24%%-----------------------------------------------------------------
25-module(megaco_udp).
26
27-include_lib("megaco/include/megaco.hrl").
28-include_lib("megaco/src/udp/megaco_udp.hrl").
29
30-record(send_handle, {socket, addr, port}).
31
32
33%%-----------------------------------------------------------------
34%% External exports
35%%-----------------------------------------------------------------
36-export([
37	 start_transport/0, stop_transport/1,
38	 open/2,
39	 socket/1,
40	 create_send_handle/3,
41	 send_message/2,
42	 close/1,
43	 block/1,
44	 unblock/1,
45
46         upgrade_receive_handle/2
47	]).
48
49%% Statistics exports
50-export([get_stats/0, get_stats/1, get_stats/2,
51	 reset_stats/0, reset_stats/1]).
52
53
54%%-----------------------------------------------------------------
55%% External interface functions
56%%-----------------------------------------------------------------
57
58%%-----------------------------------------------------------------
59%% Func: get_stats/0, get_stats/1, get_stats/2
60%% Description: Retreive statistics (counters) for TCP
61%%-----------------------------------------------------------------
62get_stats() ->
63    megaco_stats:get_stats(megaco_udp_stats).
64
65get_stats(SH) when is_record(SH, send_handle) ->
66    megaco_stats:get_stats(megaco_udp_stats, SH).
67
68get_stats(SH, Counter)
69  when is_record(SH, send_handle) andalso is_atom(Counter) ->
70    megaco_stats:get_stats(megaco_udp_stats, SH, Counter).
71
72
73%%-----------------------------------------------------------------
74%% Func: reset_stats/0, reaet_stats/1
75%% Description: Reset statistics (counters) for TCP
76%%-----------------------------------------------------------------
77reset_stats() ->
78    megaco_stats:reset_stats(megaco_udp_stats).
79
80reset_stats(SH) when is_record(SH, send_handle) ->
81    megaco_stats:reset_stats(megaco_udp_stats, SH).
82
83
84
85%%-----------------------------------------------------------------
86%% Func: start_transport
87%% Description: Starts the UDP transport service
88%%-----------------------------------------------------------------
89start_transport() ->
90    (catch megaco_stats:init(megaco_udp_stats)),
91    megaco_udp_sup:start_link().
92
93
94%%-----------------------------------------------------------------
95%% Func: stop_transport
96%% Description: Stop the UDP transport service
97%%-----------------------------------------------------------------
98stop_transport(Pid) ->
99    (catch unlink(Pid)),
100    stop_transport(Pid, shutdown).
101
102stop_transport(Pid, Reason) ->
103    exit(Pid, Reason).
104
105
106%%-----------------------------------------------------------------
107%% Func: open
108%% Description: Function is used when opening an UDP socket
109%%-----------------------------------------------------------------
110open(SupPid, Options) ->
111    Mand = [port, receive_handle],
112    case parse_options(Options, #megaco_udp{}, Mand) of
113	{ok, UdpRec} ->
114
115	    %%------------------------------------------------------
116	    %% Setup the socket
117	    IpOpts = [binary, {reuseaddr, true}, {active, once} |
118		      UdpRec#megaco_udp.options],
119
120	    case (catch gen_udp:open(UdpRec#megaco_udp.port, IpOpts)) of
121		{ok, Socket} ->
122		    ?udp_debug(UdpRec, "udp open", []),
123		    NewUdpRec = UdpRec#megaco_udp{socket = Socket},
124		    case start_udp_server(SupPid, NewUdpRec) of
125			{ok, ControlPid} ->
126			    gen_udp:controlling_process(Socket, ControlPid),
127			    {ok, Socket, ControlPid};
128			{error, Reason} ->
129			    Error = {error, {could_not_start_udp_server, Reason}},
130			    ?udp_debug({socket, Socket}, "udp close", []),
131			    gen_udp:close(Socket),
132			    Error
133
134		    end;
135		{'EXIT', Reason} ->
136		    Error = {error, {could_not_open_udp_port, Reason}},
137		    ?udp_debug(UdpRec, "udp open exited", [Error]),
138		    Error;
139		{error, Reason} ->
140		    Error = {error, {could_not_open_udp_port, Reason}},
141		    ?udp_debug(UdpRec, "udp open failed", [Error]),
142		    Error
143	    end;
144	{error, Reason} = Error ->
145	    ?udp_debug(#megaco_udp{}, "udp open failed",
146		       [Error, {options, Options}]),
147	    {error, Reason}
148    end.
149
150
151%%-----------------------------------------------------------------
152%% Func: socket
153%% Description: Returns the inet socket
154%%-----------------------------------------------------------------
155socket(SH) when is_record(SH, send_handle) ->
156    SH#send_handle.socket;
157socket(Socket) ->
158    Socket.
159
160
161upgrade_receive_handle(Pid, NewHandle)
162  when is_pid(Pid) andalso is_record(NewHandle, megaco_receive_handle) ->
163    megaco_udp_server:upgrade_receive_handle(Pid, NewHandle).
164
165
166%%-----------------------------------------------------------------
167%% Func: create_send_handle
168%% Description: Function is used for creating the handle used when
169%%    sending data on the UDP socket
170%%-----------------------------------------------------------------
171create_send_handle(Socket, {_, _, _, _} = Addr, Port) ->
172    do_create_send_handle(Socket, Addr, Port);
173create_send_handle(Socket, Addr0, Port) ->
174    {ok, Addr} = inet:getaddr(Addr0, inet),
175    do_create_send_handle(Socket, Addr, Port).
176
177do_create_send_handle(Socket, Addr, Port) ->
178    %% If neccessary create snmp counter's
179    SH = #send_handle{socket = Socket, addr = Addr, port = Port},
180    maybe_create_snmp_counters(SH),
181    SH.
182
183
184maybe_create_snmp_counters(SH) ->
185    Counters = [medGwyGatewayNumInMessages,
186		medGwyGatewayNumInOctets,
187		medGwyGatewayNumOutMessages,
188		medGwyGatewayNumOutOctets,
189		medGwyGatewayNumErrors],
190    %% Only need to check one of them, since either all of them exist
191    %% or none of them exist:
192    Key = {SH, medGwyGatewayNumInMessages},
193    case (catch ets:lookup(megaco_udp_stats, Key)) of
194	[] ->
195	    create_snmp_counters(SH, Counters);
196	[_] ->
197	    ok;
198	_ ->
199	    ok
200    end.
201
202create_snmp_counters(_SH, []) ->
203    ok;
204create_snmp_counters(SH, [Counter|Counters]) ->
205    Key = {SH, Counter},
206    ets:insert(megaco_udp_stats, {Key, 0}),
207    create_snmp_counters(SH, Counters).
208
209
210%%-----------------------------------------------------------------
211%% Func: send_message
212%% Description: Function is used for sending data on the UDP socket
213%%-----------------------------------------------------------------
214send_message(SH, Data) when is_record(SH, send_handle) ->
215    #send_handle{socket = Socket, addr = Addr, port = Port} = SH,
216    Res = gen_udp:send(Socket, Addr, Port, Data),
217    case Res of
218	ok ->
219	    incNumOutMessages(SH),
220	    incNumOutOctets(SH, size(Data));
221	_ ->
222	    ok
223    end,
224    Res;
225send_message(SH, _Data) ->
226    {error, {bad_send_handle, SH}}.
227
228
229%%-----------------------------------------------------------------
230%% Func: block
231%% Description: Function is used for blocking incomming messages
232%%              on the TCP socket
233%%-----------------------------------------------------------------
234block(SH) when is_record(SH, send_handle) ->
235    block(SH#send_handle.socket);
236block(Socket) ->
237    ?udp_debug({socket, Socket}, "udp block", []),
238    inet:setopts(Socket, [{active, false}]).
239
240
241%%-----------------------------------------------------------------
242%% Func: unblock
243%% Description: Function is used for blocking incomming messages
244%%              on the TCP socket
245%%-----------------------------------------------------------------
246unblock(SH) when is_record(SH, send_handle) ->
247    unblock(SH#send_handle.socket);
248unblock(Socket) ->
249    ?udp_debug({socket, Socket}, "udp unblock", []),
250    inet:setopts(Socket, [{active, once}]).
251
252
253%%-----------------------------------------------------------------
254%% Func: close
255%% Description: Function is used for closing the UDP socket
256%%-----------------------------------------------------------------
257close(#send_handle{socket = Socket}) ->
258    close(Socket);
259close(Socket) ->
260    ?udp_debug({socket, Socket}, "udp close", []),
261    case erlang:port_info(Socket, connected) of
262	{connected, ControlPid} ->
263	    megaco_udp_server:stop(ControlPid);
264	undefined ->
265	    {error, already_closed}
266    end.
267
268
269%%-----------------------------------------------------------------
270%% Internal functions
271%%-----------------------------------------------------------------
272%%-----------------------------------------------------------------
273%% Func: start_udp_server/1
274%% Description: Function is used for starting up a connection
275%%              process
276%%-----------------------------------------------------------------
277start_udp_server(SupPid, UdpRec) ->
278    megaco_udp_sup:start_child(SupPid, UdpRec).
279
280
281%%-----------------------------------------------------------------
282%% Func: parse_options
283%% Description: Function that parses the options sent to the UDP
284%%              module.
285%%-----------------------------------------------------------------
286parse_options([{Tag, Val} | T], UdpRec, Mand) ->
287    Mand2 = Mand -- [Tag],
288    case Tag of
289	port ->
290	    parse_options(T, UdpRec#megaco_udp{port = Val}, Mand2);
291	udp_options when is_list(Val) ->
292	    parse_options(T, UdpRec#megaco_udp{options = Val}, Mand2);
293	receive_handle ->
294	    parse_options(T, UdpRec#megaco_udp{receive_handle = Val}, Mand2);
295	module when is_atom(Val) ->
296	    parse_options(T, UdpRec#megaco_udp{module = Val}, Mand2);
297	serialize when (Val =:= true) orelse (Val =:= false) ->
298	    parse_options(T, UdpRec#megaco_udp{serialize = Val}, Mand2);
299        Bad ->
300	    {error, {bad_option, Bad}}
301    end;
302parse_options([], UdpRec, []) ->
303    {ok, UdpRec};
304parse_options([], _UdpRec, Mand) ->
305    {error, {missing_options, Mand}};
306parse_options(BadList, _UdpRec, _Mand) ->
307    {error, {bad_option_list, BadList}}.
308
309
310%%-----------------------------------------------------------------
311%% Func: incNumOutMessages/1, incNumOutOctets/2, incNumErrors/1
312%% Description: SNMP counter increment functions
313%%
314%%-----------------------------------------------------------------
315incNumOutMessages(SH) ->
316    incCounter({SH, medGwyGatewayNumOutMessages}, 1).
317
318incNumOutOctets(SH, NumOctets) ->
319    incCounter({SH, medGwyGatewayNumOutOctets}, NumOctets).
320
321incCounter(Key, Inc) ->
322    ets:update_counter(megaco_udp_stats, Key, Inc).
323
324% incNumErrors(SH) ->
325%     incCounter({SH, medGwyGatewayNumErrors}, 1).
326