osmo-epdg/src/epdg_diameter_swx.erl

185 lines
6.0 KiB
Erlang

% SWx: AAA side
%
% TS 29.273
%
% (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
% Author: Alexander Couzens <lynxis@fe80.eu>
%
% All Rights Reserved
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU Affero General Public License as
% published by the Free Software Foundation; either version 3 of the
% License, or (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU Affero General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
% Additional Permission under GNU AGPL version 3 section 7:
%
% If you modify this Program, or any covered work, by linking or
% combining it with runtime libraries of Erlang/OTP as released by
% Ericsson on http://www.erlang.org (or a modified version of these
% libraries), containing parts covered by the terms of the Erlang Public
% License (http://www.erlang.org/EPLICENSE), the licensors of this
% Program grant you additional permission to convey the resulting work
% without the need to license the runtime libraries of Erlang/OTP under
% the GNU Affero General Public License. Corresponding Source for a
% non-source form of such a combination shall include the source code
% for the parts of the runtime libraries of Erlang/OTP used as well as
% that of the covered work.
-module(epdg_diameter_swx).
-author('Alexander Couzens <lynxis@fe80.eu>').
-behaviour(gen_server).
-include_lib("diameter_3gpp_ts29_273_swx.hrl").
%% API Function Exports
-export([start_link/0]).
-export([start/0, stop/0, terminate/2]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-export([code_change/3]).
-export([media_auth_request/6]).
-export([test/0]).
%% Diameter Application Definitions
-define(SERVER, ?MODULE).
-define(SVC_NAME, ?MODULE).
-define(APP_ALIAS, ?MODULE).
-define(CALLBACK_MOD, client_cb).
-define(DIAMETER_DICT_SWX, diameter_3gpp_ts29_273_swx).
%% The service configuration. As in the server example, a client
%% supporting multiple Diameter applications may or may not want to
%% configure a common callback module on all applications.
-define(SERVICE(Name),
[{'Origin-Host', application:get_env(?SERVER, origin_host, "default.com")},
{'Origin-Realm', application:get_env(?SERVER, origin_realm, "realm.default.com")},
{'Vendor-Id', application:get_env(?SERVER, vendor_id, 0)},
{'Product-Name', "Client"},
{application,
[{alias, ?APP_ALIAS}, {dictionary, ?DIAMETER_DICT_SWX}, {module, ?CALLBACK_MOD}]}]).
-record(state, {
handlers,
peers = #{}
}).
%% @doc starts gen_server implementation process
-spec start() -> ok | {error, term()}.
start() ->
application:ensure_all_started(?MODULE),
start_link().
%% @doc stops gen_server implementation process
-spec stop() -> ok.
stop() ->
gen_server:cast(?SERVER, stop).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
peer_down(API, SvcName, {PeerRef, _} = Peer) ->
(catch ets:delete(?MODULE, {API, PeerRef})),
gen_server:cast(?SERVER, {peer_down, SvcName, Peer}),
ok.
init(State) ->
Proto = application:get_env(?SERVER, diameter_proto, sctp),
Ip = application:get_env(?SERVER, diameter_server_ip, "192.168.56.132"),
Port = application:get_env(?SERVER, diameter_port, 3868),
DiaServ = diameter:start_service(?MODULE, ?SERVICE(Name)),
lager:info("DiaServices is ~p~n", [DiaServ]),
Transport = connect({address, Proto, Ip, Port}),
lager:info("DiaTransport is ~p~n", [Transport]),
{ok, State}.
test() ->
media_auth_request("123456789012345", 3, "AKA", 1, [], []).
media_auth_request(IMSI, NumAuthItems, AuthScheme, RAT, CKey = [], IntegrityKey = []) ->
Res = gen_server:call(?SERVER,
{mar, {IMSI, NumAuthItems, AuthScheme, RAT, CKey, IntegrityKey}}),
lager:info("Response is ~p~n", [Res]),
Res.
% TODO Sync failure
handle_call({mar, {IMSI, NumAuthItems, AuthScheme, RAT, _CKey, _IntegrityKey}}, _From, State) ->
SessionId = diameter:session_id(atom_to_list(?SVC_NAME)),
MAR = #'MAR'{'Session-Id' = SessionId,
'Auth-Session-State' = 1,
'User-Name' = IMSI,
'SIP-Auth-Data-Item' = #'SIP-Auth-Data-Item'{'SIP-Authentication-Scheme' = AuthScheme},
'SIP-Number-Auth-Items' = NumAuthItems,
'RAT-Type' = RAT
},
Ret = diameter:call(?SVC_NAME, ?APP_ALIAS, MAR, []),
case Ret of
{ok, MAA} ->
lager:info("MAR Success"),
{reply, {ok, MAA}, State};
{error, Err} ->
lager:error("Error: ~w~n", [Err]),
{reply, {error, Err}, State}
end.
%% @callback gen_server
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Req, State) ->
{noreply, State}.
%% @callback gen_server
handle_info(_Info, State) ->
{noreply, State}.
%% @callback gen_server
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% @callback gen_server
terminate(normal, _State) ->
diameter:stop_service(?SVC_NAME),
ok;
terminate(shutdown, _State) ->
ok;
terminate({shutdown, _Reason}, _State) ->
ok;
terminate(_Reason, _State) ->
ok.
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
%% connect/2
connect(Name, {address, Protocol, IPAddr, Port}) ->
{ok, IP} = inet_parse:address(IPAddr),
TransportOpts =
[{transport_module, tmod(Protocol)},
{transport_config,
[{reuseaddr, true},
{raddr, IP},
{rport, Port}]}],
diameter:add_transport(Name, {connect, [{reconnect_timer, 1000} | TransportOpts]}).
connect(Address) ->
connect(?SVC_NAME, Address).
%% Convert connection type
tmod(tcp) ->
diameter_tcp;
tmod(sctp) ->
diameter_sctp.