pespin has uploaded this change for review.

View Change

WIP: Implement RTR Deregistration-Reason PERMANENT_TERMINATION

Change-Id: I57f2e02dc4034b63c118e4a4139b2830e38a2138
---
M config/sys.config
M src/aaa_diameter_s6b.erl
M src/aaa_diameter_s6b_cb.erl
M src/aaa_diameter_swm.erl
M src/aaa_diameter_swx_cb.erl
M src/aaa_ue_fsm.erl
M src/conv.erl
M src/epdg_diameter_swm.erl
M src/epdg_ue_fsm.erl
9 files changed, 294 insertions(+), 35 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-epdg refs/changes/62/36262/1
diff --git a/config/sys.config b/config/sys.config
index a40fd23..2677c3d 100755
--- a/config/sys.config
+++ b/config/sys.config
@@ -28,6 +28,7 @@
{dia_s6b_watchdog_config,
[{okay, 3},
{suspect, 1}]},
+ {dia_s6b_transmit_timer, 10000},
{dia_s6b_vendor_id, 0},
{dia_s6b_origin_host, "aaa.localdomain"},
{dia_s6b_origin_realm, "localdomain"},
diff --git a/src/aaa_diameter_s6b.erl b/src/aaa_diameter_s6b.erl
index baf251f..c9afc37 100644
--- a/src/aaa_diameter_s6b.erl
+++ b/src/aaa_diameter_s6b.erl
@@ -49,6 +49,7 @@
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, peer_down/3]).
-export([code_change/3]).
+-export([tx_as_request/1]).
-export([tx_aa_answer/2, tx_st_answer/2]).

%% Diameter Application Definitions
@@ -68,6 +69,7 @@
-define(ENV_DEFAULT_DIAMETER_CONNECT_TIMER_MS, 30000).
-define(ENV_DEFAULT_DIAMETER_WATCHDOG_TIMER_MS, 30000).
-define(ENV_DEFAULT_DIAMETER_WATCHDOG_CFG, [{okay, 3}, {suspect, 1}]).
+-define(ENV_DEFAULT_DIAMETER_TRANSMIT_TIMER_MS, 10000).

-define(VENDOR_ID_3GPP, 10415).
-define(VENDOR_ID_3GPP2, 5535).
@@ -93,6 +95,10 @@
{module, ?CALLBACK_MOD},
{answer_errors, callback}]}]).

+-record(s6b_state, {
+ tx_timeout :: non_neg_integer()
+}).
+
%% @doc starts gen_server implementation process
-spec start() -> ok | {error, term()}.
start() ->
@@ -111,17 +117,18 @@
gen_server:cast(?SERVER, {peer_down, SvcName, Peer}),
ok.

-init(State) ->
+init([]) ->
Proto = application:get_env(?ENV_APP_NAME, dia_s6b_proto, ?ENV_DEFAULT_DIAMETER_PROTO),
Ip = application:get_env(?ENV_APP_NAME, dia_s6b_local_ip, ?ENV_DEFAULT_DIAMETER_REMOTE_IP),
Port = application:get_env(?ENV_APP_NAME, dia_s6b_local_port, ?ENV_DEFAULT_DIAMETER_REMOTE_PORT),
ConnectTimer = application:get_env(?ENV_APP_NAME, dia_s6b_connect_timer, ?ENV_DEFAULT_DIAMETER_CONNECT_TIMER_MS),
WatchdogTimer = application:get_env(?ENV_APP_NAME, dia_s6b_watchdog_timer, ?ENV_DEFAULT_DIAMETER_WATCHDOG_TIMER_MS),
WatchdogConfig = application:get_env(?ENV_APP_NAME, diameter_watchdog_config, ?ENV_DEFAULT_DIAMETER_WATCHDOG_CFG),
+ TxTimer = application:get_env(?ENV_APP_NAME, dia_s6b_transmit_timer, ?ENV_DEFAULT_DIAMETER_TRANSMIT_TIMER_MS),
ok = diameter:start_service(?MODULE, ?SERVICE),
% lager:info("DiaServices is ~p~n", [DiaServ]),
{ok, _} = listen({address, Proto, Ip, Port}, {timer, ConnectTimer, WatchdogTimer, WatchdogConfig}),
- {ok, State}.
+ {ok, #s6b_state{tx_timeout = TxTimer}}.

tx_aa_answer(Pid, DiaRC) ->
% handle_request(AAR) was spawned into its own process, and it's blocked waiting for AAA:
@@ -131,6 +138,27 @@
% handle_request(STR) was spawned into its own process, and it's blocked waiting for STA:
Pid ! {sta, DiaRC}.

+tx_as_request(Imsi) ->
+ gen_server:call(?SERVER, {asr, Imsi}).
+
+handle_call({asr, Imsi}, _From, State) ->
+ lager:debug("S6b Tx ASR Imsi=~p~n", [Imsi]),
+ SessionId = diameter:session_id(application:get_env(?ENV_APP_NAME, dia_s6b_origin_host, ?ENV_DEFAULT_ORIG_HOST)),
+ ASR = #'ASR'{'Session-Id' = SessionId,
+ 'Auth-Application-Id' = ?DIAMETER_APP_ID_S6b,
+ 'User-Name' = Imsi,
+ 'Auth-Session-State' = ?'AUTH-SESSION-STATE_NO_STATE_MAINTAINED'
+ },
+ lager:debug("S6b Tx ASR: ~p~n", [ASR]),
+ Ret = diameter_call(ASR, State),
+ case Ret of
+ ok ->
+ {reply, ok, State};
+ {error, Err} ->
+ lager:error("Error: ~w~n", [Err]),
+ {reply, {error, Err}, State}
+ end;
+
handle_call(Info, _From, State) ->
error_logger:error_report(["unknown handle_call", {module, ?MODULE}, {info, Info}, {state, State}]).

@@ -189,4 +217,7 @@
tmod(sctp) ->
diameter_sctp.

+diameter_call(Msg, State) ->
+ diameter:call(?SVC_NAME, ?APP_ALIAS, Msg, [{timeout, State#s6b_state.tx_timeout},
+ detach]).

diff --git a/src/aaa_diameter_s6b_cb.erl b/src/aaa_diameter_s6b_cb.erl
index a56a7ca..ad50e74 100644
--- a/src/aaa_diameter_s6b_cb.erl
+++ b/src/aaa_diameter_s6b_cb.erl
@@ -29,23 +29,31 @@
{ok, Peer}.

%% prepare_request/3
-prepare_request(_Req, _SvcName, _Peer) ->
- lager:error("Unexpected prepare_request(): ~p~n", [_Req]),
- ?UNEXPECTED.
+prepare_request(#diameter_packet{msg = [ T | Avps ]}, _, {_, Caps})
+ when is_list(Avps) ->
+ #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps,
+ {send,
+ [T,
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', [DH]},
+ {'Destination-Realm', DR}
+ | Avps]};
+% TODO: is there a simple way to capture all the following requests?
+prepare_request(#diameter_packet{msg = Req}, _, {_, Caps})
+ when is_record(Req, 'ASR') ->
+ #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps,
+ Msg = Req#'ASR'{'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Destination-Realm' = DR,
+ 'Destination-Host' = DH},
+ lager:debug("S6b prepare_request: ~p~n", [Msg]),
+ {send, Msg}.

%% prepare_retransmit/3
prepare_retransmit(Packet, SvcName, Peer) ->
prepare_request(Packet, SvcName, Peer).

-%% handle_answer/4
-
-%% Since client.erl has detached the call when using the list
-%% encoding and not otherwise, output to the terminal in the
-%% the former case, return in the latter.
-
-handle_answer(_Packet, _Request, _SvcName, _Peer) ->
- ?UNEXPECTED.
-
%% handle_error/4
handle_error(Reason, Request, _SvcName, _Peer) when is_list(Request) ->
lager:error("Request error: ~p~n", [Reason]),
@@ -123,4 +131,21 @@

handle_request(Packet, _SvcName, Peer) ->
lager:error("S6b Rx unexpected msg from ~p: ~p~n", [Peer, Packet]),
- erlang:error({unexpected, ?MODULE, ?LINE}).
\ No newline at end of file
+ erlang:error({unexpected, ?MODULE, ?LINE}).
+
+%% handle_answer/4
+handle_answer(#diameter_packet{msg = Msg, errors = Errors}, _Request, _SvcName, Peer) when is_record(Msg, 'ASA') ->
+ lager:info("S6b Rx ASA ~p: ~p/ Errors ~p ~n", [Peer, Msg, Errors]),
+ #'ASA'{'Result-Code' = ResultCode} = Msg,
+ DiaRC = #epdg_dia_rc{result_code = ResultCode},
+ case conv:dia_rc_success(DiaRC) of
+ ok ->
+ aaa_ue_fsm:ev_rx_s6b_asa(ok);
+ _ ->
+ aaa_ue_fsm:ev_rx_s6b_asa({error, DiaRC})
+ end,
+ {ok, Msg};
+
+handle_answer(#diameter_packet{msg = Msg, errors = []}, _Request, _SvcName, Peer) ->
+ lager:notice("S6b Rx unexpected ~p: ~p~n", [Peer, Msg]),
+ {ok, Msg}.
\ No newline at end of file
diff --git a/src/aaa_diameter_swm.erl b/src/aaa_diameter_swm.erl
index 2e39048..db06e30 100644
--- a/src/aaa_diameter_swm.erl
+++ b/src/aaa_diameter_swm.erl
@@ -14,8 +14,8 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-export([code_change/3, terminate/2]).

--export([auth_request/4, auth_compl_request/2, session_termination_request/1]).
--export([auth_response/2, auth_compl_response/2, session_termination_answer/2]).
+-export([auth_request/4, auth_compl_request/2, session_termination_request/1, abort_session_answer/1]).
+-export([auth_response/2, auth_compl_response/2, session_termination_answer/2, tx_as_request/1]).

-define(SERVER, ?MODULE).

@@ -38,6 +38,9 @@
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:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -50,6 +53,9 @@
session_termination_request(Imsi) ->
gen_server:cast(?SERVER, {str, Imsi}).

+abort_session_answer(Imsi) ->
+ gen_server:cast(?SERVER, {asa, Imsi}).
+
handle_cast({epdg_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);
@@ -85,6 +91,15 @@
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}.
@@ -105,6 +120,10 @@
epdg_diameter_swm:session_termination_answer(Imsi, DiaRC),
{reply, ok, State};

+handle_call({asr, Imsi}, _From, State) ->
+ epdg_diameter_swm: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}.
diff --git a/src/aaa_diameter_swx_cb.erl b/src/aaa_diameter_swx_cb.erl
index 4936c24..c6fe51f 100644
--- a/src/aaa_diameter_swx_cb.erl
+++ b/src/aaa_diameter_swx_cb.erl
@@ -78,10 +78,18 @@
'Vendor-Specific-Application-Id' = VendorAppId,
'Auth-Session-State' = AuthSessState,
'User-Name' = Imsi,
- 'Deregistration-Reason' = _DeregReason} = Req,
+ 'Deregistration-Reason' = DeregReason} = Req,
case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
Pid when is_pid(Pid) ->
- aaa_ue_fsm:stop(Pid),
+ case DeregReason of
+ #'Deregistration-Reason'{'Reason-Code' = ?'REASON-CODE_PERMANENT_TERMINATION'} ->
+ case aaa_ue_fsm:ev_rx_swx_rtr(Pid) of
+ {error, _} -> aaa_ue_fsm:stop(Pid);
+ _ -> ok
+ end;
+ _ ->
+ aaa_ue_fsm:stop(Pid)
+ end,
Res = 2001, %% Success
ERes = [];
undefined ->
@@ -109,7 +117,7 @@
#'MAA'{'Result-Code' = ResultCodeOpt,
'Experimental-Result' = ExperimentalResultOpt} = Msg,
DiaRC = parse_epdg_dia_rc(ResultCodeOpt, ExperimentalResultOpt),
- case dia_rc_success(DiaRC) of
+ case conv:dia_rc_success(DiaRC) of
ok ->
#'MAA'{'SIP-Auth-Data-Item' = SipAuthTuples} = Msg,
AuthTuples = lists:map(fun dia_sip2epdg_auth_tuple/1, SipAuthTuples),
@@ -127,7 +135,7 @@
#'SAA'{'Result-Code' = ResultCodeOpt,
'Experimental-Result' = ExperimentalResultOpt} = Msg,
DiaRC = parse_epdg_dia_rc(ResultCodeOpt, ExperimentalResultOpt),
- case dia_rc_success(DiaRC) of
+ case conv:dia_rc_success(DiaRC) of
ok ->
#'SAA'{'Non-3GPP-User-Data' = N3UA} = Msg,
PGWAddresses = parse_pgw_addr_from_N3UA(N3UA),
@@ -165,10 +173,6 @@
%% Internal Function Definitions
%% ------------------------------------------------------------------

-dia_rc_success(#epdg_dia_rc{result_code = 2001}) -> ok;
-dia_rc_success(#epdg_dia_rc{result_code = 2002}) -> ok;
-dia_rc_success(_) -> invalid_result_code.
-
parse_epdg_dia_rc([], []) ->
#epdg_dia_rc{vendor_id = undefined, result_code = 2001 };
parse_epdg_dia_rc([ResultCode], []) ->
diff --git a/src/aaa_ue_fsm.erl b/src/aaa_ue_fsm.erl
index 52057ba..0788985 100644
--- a/src/aaa_ue_fsm.erl
+++ b/src/aaa_ue_fsm.erl
@@ -42,9 +42,19 @@
-export([start/1, stop/1]).
-export([init/1,callback_mode/0,terminate/3]).
-export([get_server_name_by_imsi/1, get_pid_by_imsi/1]).
--export([ev_swm_auth_req/2, ev_swm_auth_compl/2, ev_rx_swm_str/1, ev_rx_swx_maa/2, ev_rx_swx_saa/2,
- ev_rx_s6b_aar/2, ev_rx_s6b_str/1]).
--export([state_new/3, state_wait_swx_maa/3, state_wait_swx_saa/3, state_authenticated/3, state_authenticated_wait_swx_saa/3]).
+-export([ev_swm_auth_req/2, ev_swm_auth_compl/2, ev_rx_swm_str/1, ev_rx_swm_asa/1,
+ ev_rx_swx_maa/2, ev_rx_swx_saa/2, ev_rx_swx_rtr/1,
+ ev_rx_s6b_aar/2, ev_rx_s6b_str/1, ev_rx_s6b_asa/2]).
+-export([state_new/3,
+ state_wait_swx_maa/3,
+ state_wait_swx_saa/3,
+ state_authenticated/3,
+ state_authenticated_wait_swx_saa/3,
+ state_dereg_net_initiated_wait_s6b_asa/3,
+ state_dereg_net_initiated_wait_swm_asa/3]).
+
+-define(TIMEOUT_VAL_WAIT_S6b_ANSWER, 10000).
+-define(TIMEOUT_VAL_WAIT_SWm_ANSWER, 10000).

-record(ue_fsm_data, {
imsi = unknown :: string(),
@@ -102,6 +112,15 @@
{error, Err}
end.

+ev_rx_swm_asa(Pid) ->
+ lager:info("ue_fsm ev_rx_swm_asa~n", []),
+ try
+ gen_statem:call(Pid, rx_swm_asa)
+ catch
+ exit:Err ->
+ {error, Err}
+ end.
+
ev_rx_swx_maa(Pid, Result) ->
lager:info("ue_fsm ev_rx_swx_maa~n", []),
try
@@ -120,6 +139,15 @@
{error, Err}
end.

+ev_rx_swx_rtr(Pid) ->
+ lager:info("ue_fsm ev_rx_swx_rtr~n", []),
+ try
+ gen_statem:call(Pid, rx_swx_rtr)
+ catch
+ exit:Err ->
+ {error, Err}
+ end.
+
ev_rx_s6b_aar(Pid, {Apn, AgentInfoOpt}) ->
lager:info("ue_fsm ev_rx_s6b_aar: ~p ~p~n", [Apn, AgentInfoOpt]),
try
@@ -129,6 +157,15 @@
{error, Err}
end.

+ev_rx_s6b_asa(Pid, Result) ->
+ lager:info("ue_fsm ev_rx_s6b_asa: ~p~n", [Result]),
+ try
+ gen_statem:call(Pid, {rx_s6b_asa, Result})
+ catch
+ exit:Err ->
+ {error, Err}
+ end.
+
ev_rx_s6b_str(Pid) ->
lager:info("ue_fsm ev_rx_s6b_str~n", []),
try
@@ -223,7 +260,7 @@
DiaRC = 5002, %% UNKNOWN_SESSION_ID
{keep_state, Data, [{reply,From,{error, DiaRC}}]};
{true, true} -> %% The other session is still active, no need to send SAR Type=USER_DEREGISTRATION
- lager:info("ue_fsm state_authenticated event=rx_swn_str: PGW session still active, skip updating the HSS~n", []),
+ lager:info("ue_fsm state_authenticated event=rx_swm_str: PGW session still active, skip updating the HSS~n", []),
Data1 = Data#ue_fsm_data{epdg_sess_active = false},
{keep_state, Data1, [{reply,From,{ok, 2001}}]};
{true, false} -> %% All sessions will now be gone, trigger SAR Type=USER_DEREGISTRATION
@@ -264,6 +301,13 @@
lager:info("ue_fsm state_authenticated event=swm_auth_req {~p, ~p, ~p}, ~p~n", [PdpTypeNr, Apn, EAP, Data]),
{next_state, state_new, Data, [postpone]};

+state_authenticated({call, From}, rx_swx_rtr, Data) ->
+ lager:info("ue_fsm state_authenticated event=rx_swx_rtr ~p~n", [Data]),
+ case {Data#ue_fsm_data.pgw_sess_active, Data#ue_fsm_data.epdg_sess_active} of
+ {true, _} -> {next_state, state_dereg_net_initiated_wait_s6b_asa, Data, [{reply,From,ok}]};
+ {false, _} -> {next_state, state_dereg_net_initiated_wait_s6b_asa, Data, [{reply,From,ok}]} %% TODO: proper state for s6b
+ end;
+
state_authenticated({call, From}, Ev, Data) ->
lager:info("ue_fsm state_authenticated: Unexpected call event ~p, ~p~n", [Ev, Data]),
{keep_state, Data, [{reply,From,ok}]}.
@@ -293,4 +337,36 @@
Data1 = Data#ue_fsm_data{pgw_sess_active = false, s6b_resp_pid = undefined},
{next_state, state_new, Data1, [{reply,From,ok}]}
end
- end.
\ No newline at end of file
+ end.
+
+%% HSS asked us to do deregistration towards the user.
+%% Transmit S6b ASR towards PGW and wait for ASA back.
+state_dereg_net_initiated_wait_s6b_asa(enter, _OldState, Data) ->
+ aaa_diameter_s6b:tx_as_request(Data#ue_fsm_data.imsi),
+ {keep_state, Data, {state_timeout,?TIMEOUT_VAL_WAIT_S6b_ANSWER,s6b_asa_timeout}};
+
+state_dereg_net_initiated_wait_s6b_asa({call, From}, {rx_s6b_asa, _Result}, Data) ->
+ {next_state, state_dereg_net_initiated_wait_swm_asa, Data, [{reply,From,ok}]};
+
+state_dereg_net_initiated_wait_s6b_asa({call, From}, Ev, Data) ->
+ lager:info("ue_fsm state_dereg_net_initiated_wait_s6b_asa: Unexpected call event ~p, ~p~n", [Ev, Data]),
+ {keep_state, Data, [{reply,From,ok}]};
+
+state_dereg_net_initiated_wait_s6b_asa(state_timeout, s6b_asa_timeout, Data) ->
+ {next_state, state_dereg_net_initiated_wait_swm_asa, Data}.
+
+%% HSS asked us to do deregistration towards the user.
+%% S6b (PGW) was already torn down. Now transmit SWm ASR towards ePDG and wait for ASA back.
+state_dereg_net_initiated_wait_swm_asa(enter, _OldState, Data) ->
+ aaa_diameter_swm:tx_as_request(Data#ue_fsm_data.imsi),
+ {keep_state, Data, {state_timeout,?TIMEOUT_VAL_WAIT_SWm_ANSWER,swm_asa_timeout}};
+
+state_dereg_net_initiated_wait_swm_asa({call, From}, {rx_swm_asa, _Result}, Data) ->
+ {stop_and_reply, normal, [{reply,From,ok}], Data};
+
+state_dereg_net_initiated_wait_swm_asa({call, From}, Ev, Data) ->
+ lager:info("ue_fsm state_dereg_net_initiated_wait_swm_asa: Unexpected call event ~p, ~p~n", [Ev, Data]),
+ {keep_state, Data, [{reply,From,ok}]};
+
+state_dereg_net_initiated_wait_swm_asa(state_timeout, swm_asa_timeout, _Data) ->
+ {stop, normal}.
diff --git a/src/conv.erl b/src/conv.erl
index 1a15a3c..0c08a4d 100644
--- a/src/conv.erl
+++ b/src/conv.erl
@@ -40,7 +40,7 @@

-export([ip_to_bin/1, bin_to_ip/1]).
-export([cause_gtp2gsup/1]).
--export([dia_rc_to_gsup_cause/1]).
+-export([dia_rc_success/1, dia_rc_to_gsup_cause/1]).
-export([gtp2_paa_to_epdg_eua/1, epdg_eua_to_gsup_pdp_address/1]).
-export([nai_to_imsi/1]).

@@ -92,6 +92,10 @@
-define(DIAMETER_ERROR_EQUIPMENT_UNKNOWN, 5422).
-define(DIAMETER_ERROR_UNKOWN_SERVING_NODE, 5423).

+dia_rc_success(#epdg_dia_rc{result_code = 2001}) -> ok;
+dia_rc_success(#epdg_dia_rc{result_code = 2002}) -> ok;
+dia_rc_success(_) -> invalid_result_code.
+
-spec dia_rc_to_gsup_cause(#epdg_dia_rc{}) -> non_neg_integer().
dia_rc_to_gsup_cause(#epdg_dia_rc{result_code = 2001}) -> 0;
dia_rc_to_gsup_cause(#epdg_dia_rc{result_code = 2002}) -> 0;
diff --git a/src/epdg_diameter_swm.erl b/src/epdg_diameter_swm.erl
index c2babe3..ef70b9c 100644
--- a/src/epdg_diameter_swm.erl
+++ b/src/epdg_diameter_swm.erl
@@ -19,8 +19,10 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-export([code_change/3, terminate/2]).

--export([auth_request/4, auth_compl_request/2, session_termination_request/1]).
--export([auth_response/2, auth_compl_response/2, session_termination_answer/2]).
+-export([auth_request/4, auth_compl_request/2,
+ session_termination_request/1, abort_session_answer/1]).
+-export([auth_response/2, auth_compl_response/2,
+ session_termination_answer/2, abort_session_request/1]).

-define(SERVER, ?MODULE).

@@ -72,6 +74,17 @@
_ -> Result
end.

+% 3GPP TS 29.273 7.1.2.4
+abort_session_answer(Imsi) ->
+ % In Diameter we use Imsi as strings, as done by diameter module.
+ ImsiStr = binary_to_list(Imsi),
+ Result = gen_server:call(?SERVER, {asa, ImsiStr}),
+ case Result of
+ {ok, _Mar} ->
+ ok;
+ _ -> Result
+ end.
+
handle_call({epdg_auth_req, Imsi, PdpTypeNr, Apn, EAP}, {Pid, _Tag} = _From, State0) ->
% we yet don't implement the Diameter SWm interface on the wire, we process the call internally:
{_Sess, State1} = find_or_new_swm_session(Imsi, Pid, State0),
@@ -98,6 +111,17 @@
undefined ->
Reply = {error,unknown_imsi}
end,
+ {reply, Reply, State};
+
+handle_call({asa, Imsi}, _From, State) ->
+ % we yet don't implement the Diameter SWm interface on the wire, we process the call internally:
+ Sess = find_swm_session_by_imsi(Imsi, State),
+ case Sess of
+ #swm_session{imsi = Imsi} ->
+ Reply = aaa_diameter_swm:abort_session_answer(Imsi);
+ undefined ->
+ Reply = {error,unknown_imsi}
+ end,
{reply, Reply, State}.

handle_cast({epdg_auth_resp, Imsi, Result}, State) ->
@@ -130,6 +154,16 @@
end,
{noreply, State};

+handle_cast({asr, Imsi}, State) ->
+ Sess = find_swm_session_by_imsi(Imsi, State),
+ case Sess of
+ #swm_session{imsi = Imsi} ->
+ epdg_ue_fsm:received_swm_abort_session_request(Sess#swm_session.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}.
@@ -160,6 +194,10 @@
session_termination_answer(Imsi, Result) ->
ok = gen_server:cast(?SERVER, {sta, Imsi, Result}).

+% Rx SWm Diameter ASR:
+abort_session_request(Imsi) ->
+ ok = gen_server:cast(?SERVER, {asr, Imsi}).
+
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
diff --git a/src/epdg_ue_fsm.erl b/src/epdg_ue_fsm.erl
index a422bea..7b565b3 100644
--- a/src/epdg_ue_fsm.erl
+++ b/src/epdg_ue_fsm.erl
@@ -43,11 +43,13 @@
-export([init/1,callback_mode/0,terminate/3]).
-export([get_server_name_by_imsi/1, get_pid_by_imsi/1]).
-export([auth_request/2, lu_request/1, tunnel_request/2, purge_ms_request/1]).
--export([received_swm_auth_response/2, received_swm_auth_compl_response/2, received_swm_session_termination_answer/2]).
+-export([received_swm_auth_response/2, received_swm_auth_compl_response/2,
+ received_swm_session_termination_answer/2, received_swm_abort_session_request/1]).
-export([received_gtpc_create_session_response/2, received_gtpc_delete_session_response/2, received_gtpc_delete_bearer_request/1]).
-export([state_new/3, state_wait_auth_resp/3, state_authenticating/3, state_authenticated/3,
state_wait_create_session_resp/3, state_wait_delete_session_resp/3,
- state_wait_swm_session_termination_answer/3, state_active/3]).
+ state_wait_swm_session_termination_answer/3, state_active/3,
+ state_dereg_net_initiated_wait_s2b_delete_session_resp/3]).

-define(TIMEOUT_VAL_WAIT_GTP_ANSWER, 10000).

@@ -144,6 +146,15 @@
{error, Err}
end.

+received_swm_abort_session_request(Pid) ->
+ lager:info("ue_fsm received_swm_abort_session_request~n", []),
+ try
+ gen_statem:call(Pid, received_swm_asr)
+ catch
+ exit:Err ->
+ {error, Err}
+ end.
+
received_gtpc_create_session_response(Pid, Result) ->
lager:info("ue_fsm received_gtpc_create_session_response ~p~n", [Result]),
try
@@ -360,6 +371,14 @@
Data1 = Data#ue_fsm_data{tun_pdp_ctx = undefined, tear_down_gsup_needed = false},
{next_state, state_wait_swm_session_termination_answer, Data1, [{reply,From,ok}]};

+%%% network (HSS/AAA) initiated de-registation requested:
+state_active({call, From}, received_swm_asr, Data) ->
+ lager:info("ue_fsm state_active event=received_swm_asr, ~p~n", [Data]),
+ gsup_server:cancel_location_request(Data#ue_fsm_data.imsi),
+ gtp_u_tun:delete_pdp_context(Data#ue_fsm_data.tun_pdp_ctx),
+ Data1 = Data#ue_fsm_data{tun_pdp_ctx = undefined, tear_down_gsup_needed = false},
+ {next_state, state_dereg_net_initiated_wait_s2b_delete_session_resp, Data1, [{reply,From,ok}]};
+
state_active({call, From}, Event, Data) ->
lager:error("ue_fsm state_active: Unexpected call event ~p, ~p~n", [Event, Data]),
{keep_state, Data, [{reply,From,ok}]};
@@ -418,3 +437,36 @@
state_wait_swm_session_termination_answer({call, From}, Event, Data) ->
lager:error("ue_fsm state_wait_delete_session_resp: Unexpected call event ~p, ~p~n", [Event, Data]),
{keep_state, Data, [{reply,From,{error,unexpected_event}}]}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% state_dereg_net_initiated_wait_s2b_delete_session_resp:
+%% Network (AAA/HSS) initiated de-registration: We have informed UE (GSUP), and
+%% have triggered GTPCv1 Delete Session Req against PGW.
+%% Wait for GTPCv1 Delete Session Response, ssend SWm ASA to AAAA and terminate FSM.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+state_dereg_net_initiated_wait_s2b_delete_session_resp(enter, _OldState, Data) ->
+ case epdg_gtpc_s2b:delete_session_req(Data#ue_fsm_data.imsi) of
+ ok ->
+ {keep_state, Data, {state_timeout,?TIMEOUT_VAL_WAIT_GTP_ANSWER,s2b_delete_session_timeout}};
+ {error, Err} ->
+ epdg_diameter_swm:abort_session_answer(Data#ue_fsm_data.imsi),
+ {stop, {error,Err}}
+ end;
+
+state_dereg_net_initiated_wait_s2b_delete_session_resp({call, From}, {received_gtpc_delete_session_response, _Resp = #gtp{version = v2, type = delete_session_response, ie = IEs}}, Data) ->
+ lager:info("ue_fsm state_wait_delete_session_resp event=state_dereg_net_initiated_wait_s2b_delete_session_resp, ~p~n", [Data]),
+ #{{v2_cause,0} := CauseIE} = IEs,
+ GtpCause = gtp_utils:enum_v2_cause(CauseIE#v2_cause.v2_cause),
+ lager:debug("Cause: GTP_atom=~p -> GTP_int=~p~n", [CauseIE#v2_cause.v2_cause, GtpCause]),
+ epdg_diameter_swm:abort_session_answer(Data#ue_fsm_data.imsi),
+ {stop_and_reply, normal, [{reply,From,ok}], Data};
+
+state_dereg_net_initiated_wait_s2b_delete_session_resp({call, From}, Event, Data) ->
+ lager:error("ue_fsm state_dereg_net_initiated_wait_s2b_delete_session_resp: Unexpected call event ~p, ~p~n", [Event, Data]),
+ {keep_state, Data, [{reply,From,ok}]};
+
+
+state_dereg_net_initiated_wait_s2b_delete_session_resp(state_timeout, s2b_delete_session_timeout, Data) ->
+ lager:error("ue_fsm state_dereg_net_initiated_wait_s2b_delete_session_resp: Timeout ~p, ~p~n", [s2b_delete_session_timeout, Data]),
+ epdg_diameter_swm:abort_session_answer(Data#ue_fsm_data.imsi),
+ {stop, normal}.

To view, visit change 36262. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: erlang/osmo-epdg
Gerrit-Branch: master
Gerrit-Change-Id: I57f2e02dc4034b63c118e4a4139b2830e38a2138
Gerrit-Change-Number: 36262
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>
Gerrit-MessageType: newchange