%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1999-2009. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% %% %%----------------------------------------------------------------- %% Purpose: Interface to the UDP transport module for Megaco/H.248 %%----------------------------------------------------------------- -module(megaco_udp). -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/src/udp/megaco_udp.hrl"). -record(send_handle, {socket, addr, port}). %%----------------------------------------------------------------- %% External exports %%----------------------------------------------------------------- -export([ start_transport/0, stop_transport/1, open/2, socket/1, create_send_handle/3, send_message/2, close/1, block/1, unblock/1, upgrade_receive_handle/2 ]). %% Statistics exports -export([get_stats/0, get_stats/1, get_stats/2, reset_stats/0, reset_stats/1]). %%----------------------------------------------------------------- %% External interface functions %%----------------------------------------------------------------- %%----------------------------------------------------------------- %% Func: get_stats/0, get_stats/1, get_stats/2 %% Description: Retreive statistics (counters) for TCP %%----------------------------------------------------------------- get_stats() -> megaco_stats:get_stats(megaco_udp_stats). get_stats(SH) when is_record(SH, send_handle) -> megaco_stats:get_stats(megaco_udp_stats, SH). get_stats(SH, Counter) when is_record(SH, send_handle) andalso is_atom(Counter) -> megaco_stats:get_stats(megaco_udp_stats, SH, Counter). %%----------------------------------------------------------------- %% Func: reset_stats/0, reaet_stats/1 %% Description: Reset statistics (counters) for TCP %%----------------------------------------------------------------- reset_stats() -> megaco_stats:reset_stats(megaco_udp_stats). reset_stats(SH) when is_record(SH, send_handle) -> megaco_stats:reset_stats(megaco_udp_stats, SH). %%----------------------------------------------------------------- %% Func: start_transport %% Description: Starts the UDP transport service %%----------------------------------------------------------------- start_transport() -> (catch megaco_stats:init(megaco_udp_stats)), megaco_udp_sup:start_link(). %%----------------------------------------------------------------- %% Func: stop_transport %% Description: Stop the UDP transport service %%----------------------------------------------------------------- stop_transport(Pid) -> (catch unlink(Pid)), stop_transport(Pid, shutdown). stop_transport(Pid, Reason) -> exit(Pid, Reason). %%----------------------------------------------------------------- %% Func: open %% Description: Function is used when opening an UDP socket %%----------------------------------------------------------------- open(SupPid, Options) -> Mand = [port, receive_handle], case parse_options(Options, #megaco_udp{}, Mand) of {ok, UdpRec} -> %%------------------------------------------------------ %% Setup the socket IpOpts = [binary, {reuseaddr, true}, {active, once} | UdpRec#megaco_udp.options], case (catch gen_udp:open(UdpRec#megaco_udp.port, IpOpts)) of {ok, Socket} -> ?udp_debug(UdpRec, "udp open", []), NewUdpRec = UdpRec#megaco_udp{socket = Socket}, case start_udp_server(SupPid, NewUdpRec) of {ok, ControlPid} -> gen_udp:controlling_process(Socket, ControlPid), {ok, Socket, ControlPid}; {error, Reason} -> Error = {error, {could_not_start_udp_server, Reason}}, ?udp_debug({socket, Socket}, "udp close", []), gen_udp:close(Socket), Error end; {'EXIT', Reason} -> Error = {error, {could_not_open_udp_port, Reason}}, ?udp_debug(UdpRec, "udp open exited", [Error]), Error; {error, Reason} -> Error = {error, {could_not_open_udp_port, Reason}}, ?udp_debug(UdpRec, "udp open failed", [Error]), Error end; {error, Reason} = Error -> ?udp_debug(#megaco_udp{}, "udp open failed", [Error, {options, Options}]), {error, Reason} end. %%----------------------------------------------------------------- %% Func: socket %% Description: Returns the inet socket %%----------------------------------------------------------------- socket(SH) when is_record(SH, send_handle) -> SH#send_handle.socket; socket(Socket) -> Socket. upgrade_receive_handle(Pid, NewHandle) when is_pid(Pid) andalso is_record(NewHandle, megaco_receive_handle) -> megaco_udp_server:upgrade_receive_handle(Pid, NewHandle). %%----------------------------------------------------------------- %% Func: create_send_handle %% Description: Function is used for creating the handle used when %% sending data on the UDP socket %%----------------------------------------------------------------- create_send_handle(Socket, {_, _, _, _} = Addr, Port) -> do_create_send_handle(Socket, Addr, Port); create_send_handle(Socket, Addr0, Port) -> {ok, Addr} = inet:getaddr(Addr0, inet), do_create_send_handle(Socket, Addr, Port). do_create_send_handle(Socket, Addr, Port) -> %% If neccessary create snmp counter's SH = #send_handle{socket = Socket, addr = Addr, port = Port}, maybe_create_snmp_counters(SH), SH. maybe_create_snmp_counters(SH) -> Counters = [medGwyGatewayNumInMessages, medGwyGatewayNumInOctets, medGwyGatewayNumOutMessages, medGwyGatewayNumOutOctets, medGwyGatewayNumErrors], %% Only need to check one of them, since either all of them exist %% or none of them exist: Key = {SH, medGwyGatewayNumInMessages}, case (catch ets:lookup(megaco_udp_stats, Key)) of [] -> create_snmp_counters(SH, Counters); [_] -> ok; _ -> ok end. create_snmp_counters(_SH, []) -> ok; create_snmp_counters(SH, [Counter|Counters]) -> Key = {SH, Counter}, ets:insert(megaco_udp_stats, {Key, 0}), create_snmp_counters(SH, Counters). %%----------------------------------------------------------------- %% Func: send_message %% Description: Function is used for sending data on the UDP socket %%----------------------------------------------------------------- send_message(SH, Data) when is_record(SH, send_handle) -> #send_handle{socket = Socket, addr = Addr, port = Port} = SH, Res = gen_udp:send(Socket, Addr, Port, Data), case Res of ok -> incNumOutMessages(SH), incNumOutOctets(SH, size(Data)); _ -> ok end, Res; send_message(SH, _Data) -> {error, {bad_send_handle, SH}}. %%----------------------------------------------------------------- %% Func: block %% Description: Function is used for blocking incomming messages %% on the TCP socket %%----------------------------------------------------------------- block(SH) when is_record(SH, send_handle) -> block(SH#send_handle.socket); block(Socket) -> ?udp_debug({socket, Socket}, "udp block", []), inet:setopts(Socket, [{active, false}]). %%----------------------------------------------------------------- %% Func: unblock %% Description: Function is used for blocking incomming messages %% on the TCP socket %%----------------------------------------------------------------- unblock(SH) when is_record(SH, send_handle) -> unblock(SH#send_handle.socket); unblock(Socket) -> ?udp_debug({socket, Socket}, "udp unblock", []), inet:setopts(Socket, [{active, once}]). %%----------------------------------------------------------------- %% Func: close %% Description: Function is used for closing the UDP socket %%----------------------------------------------------------------- close(#send_handle{socket = Socket}) -> close(Socket); close(Socket) -> ?udp_debug({socket, Socket}, "udp close", []), case erlang:port_info(Socket, connected) of {connected, ControlPid} -> megaco_udp_server:stop(ControlPid); undefined -> {error, already_closed} end. %%----------------------------------------------------------------- %% Internal functions %%----------------------------------------------------------------- %%----------------------------------------------------------------- %% Func: start_udp_server/1 %% Description: Function is used for starting up a connection %% process %%----------------------------------------------------------------- start_udp_server(SupPid, UdpRec) -> megaco_udp_sup:start_child(SupPid, UdpRec). %%----------------------------------------------------------------- %% Func: parse_options %% Description: Function that parses the options sent to the UDP %% module. %%----------------------------------------------------------------- parse_options([{Tag, Val} | T], UdpRec, Mand) -> Mand2 = Mand -- [Tag], case Tag of port -> parse_options(T, UdpRec#megaco_udp{port = Val}, Mand2); udp_options when is_list(Val) -> parse_options(T, UdpRec#megaco_udp{options = Val}, Mand2); receive_handle -> parse_options(T, UdpRec#megaco_udp{receive_handle = Val}, Mand2); module when is_atom(Val) -> parse_options(T, UdpRec#megaco_udp{module = Val}, Mand2); serialize when (Val =:= true) orelse (Val =:= false) -> parse_options(T, UdpRec#megaco_udp{serialize = Val}, Mand2); Bad -> {error, {bad_option, Bad}} end; parse_options([], UdpRec, []) -> {ok, UdpRec}; parse_options([], _UdpRec, Mand) -> {error, {missing_options, Mand}}; parse_options(BadList, _UdpRec, _Mand) -> {error, {bad_option_list, BadList}}. %%----------------------------------------------------------------- %% Func: incNumOutMessages/1, incNumOutOctets/2, incNumErrors/1 %% Description: SNMP counter increment functions %% %%----------------------------------------------------------------- incNumOutMessages(SH) -> incCounter({SH, medGwyGatewayNumOutMessages}, 1). incNumOutOctets(SH, NumOctets) -> incCounter({SH, medGwyGatewayNumOutOctets}, NumOctets). incCounter(Key, Inc) -> ets:update_counter(megaco_udp_stats, Key, Inc). % incNumErrors(SH) -> % incCounter({SH, medGwyGatewayNumErrors}, 1).