Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 1 | % simple, blocking/synchronous GSUP client |
| 2 | |
| 3 | % (C) 2019 by Harald Welte <laforge@gnumonks.org> |
| 4 | % |
| 5 | % All Rights Reserved |
| 6 | % |
| 7 | % This program is free software; you can redistribute it and/or modify |
| 8 | % it under the terms of the GNU Affero General Public License as |
| 9 | % published by the Free Software Foundation; either version 3 of the |
| 10 | % License, or (at your option) any later version. |
| 11 | % |
| 12 | % This program is distributed in the hope that it will be useful, |
| 13 | % but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | % GNU General Public License for more details. |
| 16 | % |
| 17 | % You should have received a copy of the GNU Affero General Public License |
| 18 | % along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | % |
| 20 | % Additional Permission under GNU AGPL version 3 section 7: |
| 21 | % |
| 22 | % If you modify this Program, or any covered work, by linking or |
| 23 | % combining it with runtime libraries of Erlang/OTP as released by |
| 24 | % Ericsson on http://www.erlang.org (or a modified version of these |
| 25 | % libraries), containing parts covered by the terms of the Erlang Public |
| 26 | % License (http://www.erlang.org/EPLICENSE), the licensors of this |
| 27 | % Program grant you additional permission to convey the resulting work |
| 28 | % without the need to license the runtime libraries of Erlang/OTP under |
| 29 | % the GNU Affero General Public License. Corresponding Source for a |
| 30 | % non-source form of such a combination shall include the source code |
| 31 | % for the parts of the runtime libraries of Erlang/OTP used as well as |
| 32 | % that of the covered work. |
| 33 | |
| 34 | -module(gsup_client). |
| 35 | |
| 36 | -behaviour(gen_server). |
| 37 | |
| 38 | -include_lib("osmo_gsup/include/gsup_protocol.hrl"). |
Alexander Couzens | ee090f4 | 2021-12-30 20:28:54 +0100 | [diff] [blame] | 39 | -include_lib("osmo_ss7/include/ipa.hrl"). |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 40 | |
| 41 | -define(IPAC_PROTO_EXT_GSUP, {osmo, 5}). |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 42 | -define(GSUP_TIMEOUT_MS, 5000). |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 43 | |
| 44 | -record(gsupc_state, { |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 45 | address, |
| 46 | port, |
| 47 | ccmoptions, |
| 48 | socket, |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 49 | ipa_pid |
| 50 | }). |
| 51 | |
| 52 | -export([start_link/3]). |
| 53 | |
| 54 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2]). |
| 55 | -export([code_change/3, terminate/2]). |
| 56 | |
| 57 | %% ------------------------------------------------------------------ |
| 58 | %% our exported API |
| 59 | %% ------------------------------------------------------------------ |
| 60 | |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 61 | start_link(ServerAddr, ServerPort, GsupName) -> |
| 62 | gen_server:start_link(?MODULE, [ServerAddr, ServerPort, GsupName], [{debug, [trace]}]). |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 63 | |
| 64 | %% ------------------------------------------------------------------ |
| 65 | %% gen_server Function Definitions |
| 66 | %% ------------------------------------------------------------------ |
| 67 | |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 68 | init([Address, Port, GsupName]) -> |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 69 | ipa_proto:init(), |
Pau Espin Pedrol | 89ac871 | 2023-08-24 17:12:00 +0200 | [diff] [blame] | 70 | % register the GSUP codec with the IPA core; ignore result as we might be doing this multiple times |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 71 | ipa_proto:register_codec(?IPAC_PROTO_EXT_GSUP, fun gsup_protocol:encode/1, fun gsup_protocol:decode/1), |
Harald Welte | e795af3 | 2019-08-20 20:05:36 +0200 | [diff] [blame] | 72 | lager:info("Connecting to GSUP HLR on IP ~s port ~p~n", [Address, Port]), |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 73 | CcmOptions = #ipa_ccm_options{ |
| 74 | serial_number=GsupName, |
| 75 | unit_id="0/0/0", |
| 76 | mac_address="00:00:00:00:00:00", |
| 77 | location="00:00:00:00:00:00", |
| 78 | unit_type="00:00:00:00:00:00", |
| 79 | equipment_version="00:00:00:00:00:00", |
| 80 | sw_version="00:00:00:00:00:00", |
| 81 | unit_name=GsupName |
| 82 | }, |
| 83 | State = #gsupc_state{address = Address, port = Port, ccmoptions = CcmOptions, socket = [], ipa_pid = []}, |
| 84 | case connect(State) of |
| 85 | {ok, State2} -> {ok, State2}; |
| 86 | {error, _, State2} -> {ok, State2, ?GSUP_TIMEOUT_MS} |
| 87 | end. |
| 88 | |
| 89 | connect(State) -> |
| 90 | #gsupc_state{address = Address, port = Port, ccmoptions = Options} = State, |
| 91 | case ipa_proto:connect(Address, Port, []) of |
Harald Welte | e795af3 | 2019-08-20 20:05:36 +0200 | [diff] [blame] | 92 | {ok, {Socket, IpaPid}} -> |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 93 | ipa_proto:set_ccm_options(Socket, Options), |
Harald Welte | e795af3 | 2019-08-20 20:05:36 +0200 | [diff] [blame] | 94 | lager:info("connected!~n", []), |
| 95 | true = ipa_proto:register_stream(Socket, ?IPAC_PROTO_EXT_GSUP, {process_id, self()}), |
| 96 | ipa_proto:unblock(Socket), |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 97 | {ok, State#gsupc_state{socket=Socket, ipa_pid=IpaPid}}; |
| 98 | {error, Error} -> |
| 99 | lager:info("Failed to GSUP HLR on IP ~s port ~p ~p~n", [Address, Port, Error]), |
| 100 | {error, Error, State} |
Harald Welte | e795af3 | 2019-08-20 20:05:36 +0200 | [diff] [blame] | 101 | end. |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 102 | |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 103 | % send a given GSUP message and synchronously wait for message type ExpRes or ExpErr |
| 104 | handle_call({transceive_gsup, GsupMsgTx, ExpRes, ExpErr}, _From, State) -> |
| 105 | Socket = State#gsupc_state.socket, |
| 106 | {ok, Imsi} = maps:find(imsi, GsupMsgTx), |
| 107 | ipa_proto:send(Socket, ?IPAC_PROTO_EXT_GSUP, GsupMsgTx), |
| 108 | % selective receive for only those GSUP responses we expect |
| 109 | receive |
| 110 | {ipa, Socket, ?IPAC_PROTO_EXT_GSUP, GsupMsgRx = #{message_type := ExpRes, imsi := Imsi}} -> |
| 111 | {reply, GsupMsgRx, State}; |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 112 | {ipa, Socket, ?IPAC_PROTO_EXT_GSUP, GsupMsgRx = #{message_type := ExpErr, imsi := Imsi}} -> |
| 113 | {reply, GsupMsgRx, State} |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 114 | after ?GSUP_TIMEOUT_MS -> |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 115 | {reply, timeout, State} |
| 116 | end. |
| 117 | |
| 118 | handle_cast(Info, S) -> |
| 119 | error_logger:error_report(["unknown handle_cast", {module, ?MODULE}, {info, Info}, {state, S}]), |
| 120 | {noreply, S}. |
| 121 | |
Harald Welte | e795af3 | 2019-08-20 20:05:36 +0200 | [diff] [blame] | 122 | handle_info({ipa_closed, _}, S) -> |
Alexander Couzens | be94530 | 2023-09-01 15:23:07 +0200 | [diff] [blame^] | 123 | lager:error("GSUP connection has been closed, Reconnecting in 5sec."), |
| 124 | {noreply, S, ?GSUP_TIMEOUT_MS}; |
| 125 | handle_info(timeout, S) -> |
| 126 | case connect(S) of |
| 127 | {ok, State} -> {noreply, State}; |
| 128 | {error, _, State} -> {noreply, State, ?GSUP_TIMEOUT_MS} |
| 129 | end; |
| 130 | |
Harald Welte | 51f47c0 | 2019-08-14 13:30:49 +0200 | [diff] [blame] | 131 | handle_info(Info, S) -> |
| 132 | error_logger:error_report(["unknown handle_info", {module, ?MODULE}, {info, Info}, {state, S}]), |
| 133 | {noreply, S}. |
| 134 | |
| 135 | terminate(Reason, _S) -> |
| 136 | lager:info("terminating ~p with reason ~p~n", [?MODULE, Reason]). |
| 137 | |
| 138 | code_change(_OldVsn, State, _Extra) -> |
| 139 | {ok, State}. |