% ePDG implementation of SWm Diameter interface, TS 29.273 section 7 % This interface is so far implemented through internal erlang messages against % the internal AAA Server. % (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(epdg_diameter_swm). -behaviour(gen_server). -include_lib("diameter_3gpp_ts29_273_swx.hrl"). -include("conv.hrl"). -record(swm_state, { }). -export([start_link/0]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2]). -export([code_change/3, terminate/2]). -export([tx_der_auth_request/4, tx_der_auth_compl_request/2, tx_reauth_answer/2, tx_auth_req/1, tx_session_termination_request/1, tx_abort_session_answer/1]). -export([rx_dea_auth_response/2, rx_dea_auth_compl_response/2, rx_reauth_request/1, rx_auth_answer/2, rx_session_termination_answer/2, rx_abort_session_request/1]). -define(SERVER, ?MODULE). % The ets table contains only IMSIs start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> {ok, #swm_state{}}. %% Swm Diameter message Diameter-EAP-Request, 3GPP TS 29.273 Table 7.1.2.1.1 tx_der_auth_request(Imsi, PdpTypeNr, Apn, EAP) -> % In Diameter we use Imsi as strings, as done by diameter module. ImsiStr = binary_to_list(Imsi), % PdpTypeNr: SWm Diameter AVP "UE-Local-IP-Address" % Apn: SWm Diameter AVP "Service-Selection" % EAP: SWm Diameter AVP EAP-Payload ok = gen_server:cast(?SERVER, {tx_dia, {der_auth_req, ImsiStr, PdpTypeNr, Apn, EAP}}). tx_reauth_answer(Imsi, DiaRC) -> % In Diameter we use Imsi as strings, as done by diameter module. ImsiStr = binary_to_list(Imsi), ok = gen_server:cast(?SERVER, {tx_dia, {raa, ImsiStr, DiaRC}}). % Rx "GSUP CEAI LU Req" is our way of saying Rx "Swm Diameter-EAP REQ (DER) with EAP AVP containing successuful auth": tx_der_auth_compl_request(Imsi, Apn) -> % In Diameter we use Imsi as strings, as done by diameter module. ImsiStr = binary_to_list(Imsi), ok = gen_server:cast(?SERVER, {tx_dia, {der_auth_compl_req, ImsiStr, Apn}}). % 3GPP TS 29.273 7.1.2.2 tx_auth_req(Imsi) -> % In Diameter we use Imsi as strings, as done by diameter module. ImsiStr = binary_to_list(Imsi), ok = gen_server:cast(?SERVER, {tx_dia, {aar, ImsiStr}}). % 3GPP TS 29.273 7.1.2.3 tx_session_termination_request(Imsi) -> % In Diameter we use Imsi as strings, as done by diameter module. ImsiStr = binary_to_list(Imsi), ok = gen_server:cast(?SERVER, {tx_dia, {str, ImsiStr}}). % 3GPP TS 29.273 7.1.2.4 tx_abort_session_answer(Imsi) -> % In Diameter we use Imsi as strings, as done by diameter module. ImsiStr = binary_to_list(Imsi), ok = gen_server:cast(?SERVER, {tx_dia, {asa, ImsiStr}}). %% Emulation from the wire (DIAMETER SWm), called from internal AAA Server: rx_reauth_request(Imsi) -> ok = gen_server:cast(?SERVER, {rx_dia, {rar, Imsi}}). %% Emulation from the wire (DIAMETER SWm), called from internal AAA Server: rx_auth_answer(Imsi, Result) -> ok = gen_server:cast(?SERVER, {rx_dia, {aaa, Imsi, Result}}). %% Emulation from the wire (DIAMETER SWm), called from internal AAA Server: rx_dea_auth_response(Imsi, Result) -> ok = gen_server:cast(?SERVER, {rx_dia, {dea_auth_resp, Imsi, Result}}). %Rx Swm Diameter-EAP Answer (DEA) containing APN-Configuration, triggered by %earlier Tx DER EAP AVP containing successuful auth": rx_dea_auth_compl_response(Imsi, Result) -> ok = gen_server:cast(?SERVER, {rx_dia, {dea_auth_compl_resp, Imsi, Result}}). % Rx SWm Diameter STA: rx_session_termination_answer(Imsi, Result) -> ok = gen_server:cast(?SERVER, {rx_dia, {sta, Imsi, Result}}). % Rx SWm Diameter ASR: rx_abort_session_request(Imsi) -> ok = gen_server:cast(?SERVER, {rx_dia, {asr, Imsi}}). %% ------------------------------------------------------------------ %% Internal Function Definitions %% ------------------------------------------------------------------ handle_call(Request, From, State) -> error_logger:error_report(["unknown handle_call", {module, ?MODULE}, {request, Request}, {from, From}, {state, State}]), {reply, ok, State}. handle_cast({tx_dia, {der_auth_req, Imsi, PdpTypeNr, Apn, EAP}}, State) -> % we yet don't implement the Diameter SWm interface on the wire, we process the call internally: aaa_diameter_swm:rx_der_auth_request(Imsi, PdpTypeNr, Apn, EAP), {noreply, State}; handle_cast({tx_dia, {raa, Imsi, DiaRC}}, State) -> % we yet don't implement the Diameter SWm interface on the wire, we process the call internally: aaa_diameter_swm:rx_reauth_answer(Imsi, DiaRC#epdg_dia_rc.result_code), {noreply, State}; handle_cast({tx_dia, {der_auth_compl_req, Imsi, Apn}}, State) -> % we yet don't implement the Diameter SWm interface on the wire, we process the call internally: aaa_diameter_swm:rx_der_auth_compl_request(Imsi, Apn), {noreply, State}; % 3GPP TS 29.273 7.2.2.1.3 Diameter-AA-Request (AAR) Command handle_cast({tx_dia, {aar, Imsi}}, State) -> % we yet don't implement the Diameter SWm interface on the wire, we process the call internally: aaa_diameter_swm:rx_auth_request(Imsi), {noreply, State}; handle_cast({tx_dia, {str, Imsi}}, State) -> % we yet don't implement the Diameter SWm interface on the wire, we process the call internally: aaa_diameter_swm:rx_session_termination_request(Imsi), {noreply, State}; handle_cast({tx_dia, {asa, Imsi}}, State) -> % we yet don't implement the Diameter SWm interface on the wire, we process the call internally: aaa_diameter_swm:rx_abort_session_answer(Imsi), {noreply, State}; handle_cast({rx_dia, {dea_auth_resp, ImsiStr, Result}}, State) -> Imsi = list_to_binary(ImsiStr), case epdg_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> epdg_ue_fsm:received_swm_dea_auth_response(Pid, Result); undefined -> error_logger:error_report(["unknown swm_session", {module, ?MODULE}, {imsi, Imsi}, {state, State}]) end, {noreply, State}; handle_cast({rx_dia, {dea_auth_compl_resp, ImsiStr, Result}}, State) -> Imsi = list_to_binary(ImsiStr), case epdg_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> epdg_ue_fsm:received_swm_dea_auth_compl_response(Pid, Result); undefined -> error_logger:error_report(["unknown swm_session", {module, ?MODULE}, {imsi, Imsi}, {state, State}]) end, {noreply, State}; handle_cast({rx_dia, {rar, ImsiStr}}, State) -> Imsi = list_to_binary(ImsiStr), case epdg_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> epdg_ue_fsm:received_swm_reauth_request(Pid); undefined -> lager:notice("SWm Rx RAR: unknown swm-session ~p", [Imsi]), DiaResultCode = 5002, %% UNKNOWN_SESSION_ID aaa_diameter_swm:rx_reauth_answer(ImsiStr, DiaResultCode) end, {noreply, State}; handle_cast({rx_dia, {aaa, ImsiStr, Result}}, State) -> Imsi = list_to_binary(ImsiStr), case epdg_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> epdg_ue_fsm:received_swm_auth_answer(Pid, Result); undefined -> lager:notice("SWm Rx RAR: unknown swm-session ~p", [Imsi]) end, {noreply, State}; handle_cast({rx_dia, {sta, ImsiStr, Result}}, State) -> Imsi = list_to_binary(ImsiStr), case epdg_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> epdg_ue_fsm:received_swm_session_termination_answer(Pid, Result); undefined -> error_logger:error_report(["unknown swm_session", {module, ?MODULE}, {imsi, Imsi}, {state, State}]) end, {noreply, State}; handle_cast({rx_dia, {asr, ImsiStr}}, State) -> Imsi = list_to_binary(ImsiStr), case epdg_ue_fsm:get_pid_by_imsi(Imsi) of Pid when is_pid(Pid) -> epdg_ue_fsm:received_swm_abort_session_request(Pid); undefined -> error_logger:error_report(["unknown swm_session", {module, ?MODULE}, {imsi, Imsi}, {state, State}]) 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}. 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]).