% AAA Server implementation of SWm Diameter interface, TS 29.273 section 7 % This interface is so far implemented through internal erlang messages against % the internal ePDG. % (C) 2023 by sysmocom - s.f.m.c. GmbH % Author: Pau Espin Pedrol % % 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 . % % 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 https://www.erlang.org (or a modified version of these % libraries), containing parts covered by the terms of the Erlang Public % License (https://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(aaa_diameter_swm). -behaviour(gen_server). -include_lib("diameter_3gpp_ts29_273.hrl"). -record(swm_state, { table_id % ets table id }). -export([start_link/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2]). -export([code_change/3, terminate/2]). -export([rx_der_auth_request/4, rx_der_auth_compl_request/2, rx_reauth_answer/2, rx_auth_request/1, rx_session_termination_request/1, rx_abort_session_answer/1]). -export([tx_dea_auth_response/2, tx_dea_auth_compl_response/2, tx_reauth_request/1, tx_session_termination_answer/2, tx_as_request/1]). -define(SERVER, ?MODULE). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> TableId = ets:new(auth_req, [bag, named_table]), {ok, #swm_state{table_id = TableId}}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Tx over emulated SWm wire: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% tx_dea_auth_response(Imsi, Result) -> _Result = gen_server:call(?SERVER, {dea_auth_resp, Imsi, Result}). tx_dea_auth_compl_response(Imsi, Result) -> _Result = gen_server:call(?SERVER, {dea_auth_compl_resp, Imsi, Result}). tx_reauth_request(Imsi) -> _Result = gen_server:call(?SERVER, {rar, Imsi}). tx_session_termination_answer(Imsi, Result) -> _Result = gen_server:call(?SERVER, {sta, Imsi, Result}). tx_as_request(Imsi) -> _result = gen_server:call(?SERVER, {asr, Imsi}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Rx from emulated SWm wire: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% rx_der_auth_request(Imsi, PdpTypeNr, Apn, EAP) -> gen_server:cast(?SERVER, {der_auth_req, Imsi, PdpTypeNr, Apn, EAP}). rx_der_auth_compl_request(Imsi, Apn) -> gen_server:cast(?SERVER, {der_auth_compl_req, Imsi, Apn}). rx_reauth_answer(Imsi, Result) -> gen_server:cast(?SERVER, {raa, Imsi, Result}). % 3GPP TS 29.273 7.2.2.1.3 Diameter-AA-Request (AAR) Command rx_auth_request(Imsi) -> gen_server:cast(?SERVER, {aar, Imsi}). rx_session_termination_request(Imsi) -> gen_server:cast(?SERVER, {str, Imsi}). rx_abort_session_answer(Imsi) -> gen_server:cast(?SERVER, {asa, Imsi}). %% handle_cast: Rx side handle_cast({der_auth_req, Imsi, PdpTypeNr, Apn, EAP}, State) -> case aaa_ue_fsm:get_pid_by_imsi(Imsi) of undefined -> {ok, Pid} = aaa_ue_fsm:start(Imsi); Pid -> Pid end, aaa_ue_fsm:ev_rx_swm_der_auth_req(Pid, {PdpTypeNr, Apn, EAP}), {noreply, State}; handle_cast({der_auth_compl_req, Imsi, Apn}, State) -> case aaa_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> aaa_ue_fsm:ev_rx_swm_der_auth_compl(Pid, Apn); undefined -> RC_USER_UNKNOWN=5030, epdg_diameter_swm:rx_dea_auth_compl_response(Imsi, {error, RC_USER_UNKNOWN}) end, {noreply, State}; handle_cast({raa, Imsi, Result}, State) -> case aaa_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> aaa_ue_fsm:ev_rx_swm_reauth_answer(Pid, Result); undefined -> ok end, {noreply, State}; handle_cast({aar, Imsi}, State) -> case aaa_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> case aaa_ue_fsm:ev_rx_swm_auth_request(Pid) of ok -> epdg_diameter_swm:rx_auth_answer(Imsi, ok); _ -> RC_UNABLE_TO_COMPLY=5012, epdg_diameter_swm:rx_auth_answer(Imsi, {error, RC_UNABLE_TO_COMPLY}) end; undefined -> RC_USER_UNKNOWN=5030, epdg_diameter_swm:rx_auth_answer(Imsi, {error, RC_USER_UNKNOWN}) end, {noreply, State}; handle_cast({str, Imsi}, State) -> case aaa_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> case aaa_ue_fsm:ev_rx_swm_str(Pid) of ok -> ok; % Answering delayed due to SAR+SAA towards HSS. {ok, DiaRC} when is_integer(DiaRC) -> ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, DiaRC); {error, Err} when is_integer(Err) -> ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, Err); {error, _} -> ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED') end; undefined -> ok = epdg_diameter_swm:rx_session_termination_answer(Imsi, ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED') end, {noreply, State}; handle_cast({asa, Imsi}, State) -> case aaa_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> aaa_ue_fsm:ev_rx_swm_asa(Pid); undefined -> ok end, {noreply, State}; handle_cast(Info, S) -> error_logger:error_report(["unknown handle_cast", {module, ?MODULE}, {info, Info}, {state, S}]), {noreply, S}. handle_info(Info, S) -> error_logger:error_report(["unknown handle_info", {module, ?MODULE}, {info, Info}, {state, S}]), {noreply, S}. %% handle_call: Tx side handle_call({dea_auth_resp, Imsi, Result}, _From, State) -> epdg_diameter_swm:rx_dea_auth_response(Imsi, Result), {reply, ok, State}; handle_call({dea_auth_compl_resp, Imsi, Result}, _From, State) -> epdg_diameter_swm:rx_dea_auth_compl_response(Imsi, Result), {reply, ok, State}; handle_call({rar, Imsi}, _From, State) -> epdg_diameter_swm:rx_reauth_request(Imsi), {reply, ok, State}; handle_call({sta, Imsi, DiaRC}, _From, State) -> epdg_diameter_swm:rx_session_termination_answer(Imsi, DiaRC), {reply, ok, State}; handle_call({asr, Imsi}, _From, State) -> epdg_diameter_swm:rx_abort_session_request(Imsi), {reply, ok, State}; handle_call(Request, From, S) -> error_logger:error_report(["unknown handle_call", {module, ?MODULE}, {request, Request}, {from, From}, {state, S}]), {noreply, S}. stop() -> gen_server:call(?MODULE, stop). code_change(_OldVsn, State, _Extra) -> {ok, State}. terminate(Reason, _S) -> lager:info("terminating ~p with reason ~p~n", [?MODULE, Reason]). %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------