pespin has uploaded this change for review.

View Change

Send SAR(DEREGISTRATION) to HSS when all sessions are terminated

Change-Id: I62eba8ef916d52964df4135d1031f3950b6818a2
---
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
5 files changed, 157 insertions(+), 21 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-epdg refs/changes/60/35760/1
diff --git a/src/aaa_diameter_s6b.erl b/src/aaa_diameter_s6b.erl
index 2de479c..2792b6a 100644
--- a/src/aaa_diameter_s6b.erl
+++ b/src/aaa_diameter_s6b.erl
@@ -51,7 +51,7 @@
-export([code_change/3]).
-export([multimedia_auth_request/6]).
-export([server_assignment_request/3]).
--export([tx_aa_answer/2]).
+-export([tx_aa_answer/2, tx_st_answer/2]).
-export([test/0, test/1]).

%% Diameter Application Definitions
@@ -151,6 +151,10 @@
% handle_request(AAR) was spawned into its own process, and it's blocked waiting for AAA:
Pid ! {aaa, ResultCode}.

+tx_st_answer(Pid, ResultCode) ->
+ % handle_request(STR) was spawned into its own process, and it's blocked waiting for STA:
+ Pid ! {sta, ResultCode}.
+
result_code_success(2001) -> ok;
result_code_success(2002) -> ok;
result_code_success(_) -> invalid_result_code.
diff --git a/src/aaa_diameter_s6b_cb.erl b/src/aaa_diameter_s6b_cb.erl
index b03bce1..339f735 100644
--- a/src/aaa_diameter_s6b_cb.erl
+++ b/src/aaa_diameter_s6b_cb.erl
@@ -86,10 +86,30 @@
#diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} = Caps,
#'STR'{'Session-Id' = SessionId,
'Auth-Application-Id' = _AuthAppId,
- 'User-Name' = _UserNameOpt} = Req,
+ 'Termination-Cause' = _TermCause,
+ 'User-Name' = [UserName]} = Req,
+ Result = aaa_diameter_swm:get_ue_fsm_by_imsi(UserName),
+ case Result of
+ {ok, Pid} ->
+ case aaa_ue_fsm:ev_rx_s6b_str(Pid) of
+ ok ->
+ lager:debug("Waiting for S6b STA~n", []),
+ receive
+ {sta, ResultCode} -> lager:debug("Rx STA with ResultCode=~p~n", [ResultCode])
+ end;
+ {ok, DiaRC} when is_integer(DiaRC) ->
+ ResultCode = DiaRC;
+ {error, Err} when is_integer(Err) ->
+ ResultCode = Err;
+ {error, _} ->
+ ResultCode = ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED'
+ end;
+ _ -> lager:error("Error looking up FSM for IMSI~n", [UserName]),
+ ResultCode = ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED'
+ end,
% 3GPP TS 29.273 9.2.2.3.2 Session-Termination-Answer (STA) Command:
Resp = #'STA'{'Session-Id' = SessionId,
- 'Result-Code' = 2001,
+ 'Result-Code' = ResultCode,
'Origin-Host' = OH,
'Origin-Realm' = OR},
lager:info("S6b Tx to ~p: ~p~n", [Caps, Resp]),
diff --git a/src/aaa_diameter_swm.erl b/src/aaa_diameter_swm.erl
index 68aa054..9458383 100644
--- a/src/aaa_diameter_swm.erl
+++ b/src/aaa_diameter_swm.erl
@@ -4,7 +4,7 @@
-module(aaa_diameter_swm).
-behaviour(gen_server).

--include_lib("diameter_3gpp_ts29_273_swx.hrl").
+-include_lib("diameter_3gpp_ts29_273.hrl").

-record(swm_state, {
table_id, % ets table id,
@@ -22,7 +22,7 @@
-export([get_ue_fsm_by_imsi/1]).

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

-define(SERVER, ?MODULE).

@@ -45,6 +45,9 @@
auth_compl_response(Imsi, Result) ->
_Result = gen_server:call(?SERVER, {epdg_auth_compl_resp, Imsi, Result}).

+session_termination_answer(Imsi, Result) ->
+ _Result = gen_server:call(?SERVER, {sta, Imsi, Result}).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Rx from emulated SWm wire:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -73,7 +76,21 @@
{noreply, State};

handle_cast({str, Imsi}, State) ->
- ok = epdg_diameter_swm:session_termination_answer(Imsi, 2001),
+ Sess = find_swm_session_by_imsi(Imsi, State),
+ case Sess of
+ #swm_session{} ->
+ case aaa_ue_fsm:ev_rx_swm_str(Sess#swm_session.pid) of
+ ok -> ok; % Answering delayed due to SAR+SAA towards HSS.
+ {ok, DiaRC} when is_integer(DiaRC) ->
+ ok = epdg_diameter_swm:session_termination_answer(Imsi, DiaRC);
+ {error, Err} when is_integer(Err) ->
+ ok = epdg_diameter_swm:session_termination_answer(Imsi, Err);
+ {error, _} ->
+ ok = epdg_diameter_swm:session_termination_answer(Imsi, ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED')
+ end;
+ undefined ->
+ ok = epdg_diameter_swm:session_termination_answer(Imsi, ?'RULE-FAILURE-CODE_CM_AUTHORIZATION_REJECTED')
+ end,
{noreply, State};

handle_cast(Info, S) ->
@@ -102,6 +119,10 @@
epdg_diameter_swm:auth_compl_response(Imsi, Result),
{reply, ok, State};

+handle_call({sta, Imsi, DiaRC}, _From, State) ->
+ epdg_diameter_swm:session_termination_answer(Imsi, DiaRC),
+ {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 a7b9b7c..472f1d4 100644
--- a/src/aaa_diameter_swx_cb.erl
+++ b/src/aaa_diameter_swx_cb.erl
@@ -71,10 +71,13 @@
lager:info("SWx Rx MAA ~p: ~p/ Errors ~p ~n", [Peer, Msg, Errors]),
aaa_ue_fsm:ev_rx_swx_maa(ReqPid, Msg),
{ok, Msg};
-handle_answer(#diameter_packet{msg = Msg, errors = Errors}, _Request, _SvcName, Peer, ReqPid) when is_record(Msg, 'SAA') ->
+handle_answer(#diameter_packet{msg = Msg, errors = Errors}, Request, _SvcName, Peer, ReqPid) when is_record(Msg, 'SAA') ->
lager:info("SWx Rx SAA ~p: ~p/ Errors ~p ~n", [Peer, Msg, Errors]),
+ % Recover fields from originating request:
+ #'SAR'{'Server-Assignment-Type' = SAType} = Request,
+ % Retrieve fields from answer:
#'SAA'{'Result-Code' = [ResultCode]} = Msg,
- aaa_ue_fsm:ev_rx_swx_saa(ReqPid, ResultCode),
+ aaa_ue_fsm:ev_rx_swx_saa(ReqPid, {SAType, ResultCode}),
{ok, Msg}.
handle_answer(#diameter_packet{msg = Msg, errors = []}, _Request, _SvcName, Peer) ->
lager:info("SWx Rx ~p: ~p~n", [Peer, Msg]),
diff --git a/src/aaa_ue_fsm.erl b/src/aaa_ue_fsm.erl
index e214540..c043cdb 100644
--- a/src/aaa_ue_fsm.erl
+++ b/src/aaa_ue_fsm.erl
@@ -35,15 +35,18 @@
-define(NAME, aaa_ue_fsm).

-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter_3gpp_ts29_229.hrl").
-include_lib("diameter_3gpp_ts29_273_s6b.hrl").

-export([start_link/1]).
-export([init/1,callback_mode/0,terminate/3]).
--export([ev_swm_auth_req/1, ev_swm_auth_compl/2, ev_rx_swx_maa/2, ev_rx_swx_saa/2, ev_rx_s6b_aar/2]).
+-export([ev_swm_auth_req/1, 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]).

-record(ue_fsm_data, {
imsi = unknown :: binary(),
+ apn :: string(),
epdg_sess_active = false :: boolean(),
pgw_sess_active = false :: boolean(),
s6b_resp_pid :: pid()
@@ -72,6 +75,15 @@
{error, Err}
end.

+ev_rx_swm_str(Pid) ->
+ lager:info("ue_fsm ev_rx_swm_str~n", []),
+ try
+ gen_statem:call(Pid, rx_swm_str)
+ catch
+ exit:Err ->
+ {error, Err}
+ end.
+
ev_rx_swx_maa(Pid, MAA) ->
lager:info("ue_fsm ev_rx_swx_maa~n", []),
try
@@ -81,10 +93,10 @@
{error, Err}
end.

-ev_rx_swx_saa(Pid, ResultCode) ->
+ev_rx_swx_saa(Pid, {SAType, ResultCode}) ->
lager:info("ue_fsm ev_rx_swx_saa~n", []),
try
- gen_statem:call(Pid, {rx_swx_saa, ResultCode})
+ gen_statem:call(Pid, {rx_swx_saa, SAType, ResultCode})
catch
exit:Err ->
{error, Err}
@@ -99,6 +111,15 @@
{error, Err}
end.

+ev_rx_s6b_str(Pid) ->
+ lager:info("ue_fsm ev_rx_s6b_str: ~p~n", []),
+ try
+ gen_statem:call(Pid, rx_s6b_str)
+ catch
+ exit:Err ->
+ {error, Err}
+ end.
+
%% ------------------------------------------------------------------
%% Internal helpers
%% ------------------------------------------------------------------
@@ -151,9 +172,9 @@
state_wait_swx_saa(enter, _OldState, Data) ->
{keep_state, Data};

-state_wait_swx_saa({call, From}, {rx_swx_saa, SAA}, Data) ->
+state_wait_swx_saa({call, From}, {rx_swx_saa, _SAType, ResultCode}, Data) ->
lager:info("ue_fsm state_wait_swx_saa event=rx_swx_saa, ~p~n", [Data]),
- aaa_diameter_swm:auth_compl_response(Data#ue_fsm_data.imsi, {ok, SAA}),
+ aaa_diameter_swm:auth_compl_response(Data#ue_fsm_data.imsi, {ok, ResultCode}),
% TODO: don't transit if SAS returned error code.
{next_state, state_authenticated, Data, [{reply,From,ok}]}.

@@ -164,12 +185,57 @@

state_authenticated({call, {Pid, _Tag} = From}, {rx_s6b_aar, Apn}, Data) ->
lager:info("ue_fsm state_authenticated event=rx_s6b_aar Apn=~p, ~p~n", [Apn, Data]),
- case aaa_diameter_swx:server_assignment_request(Data#ue_fsm_data.imsi, 1, Apn) of
- ok -> Data1 = Data#ue_fsm_data{s6b_resp_pid = Pid},
+ case aaa_diameter_swx:server_assignment_request(Data#ue_fsm_data.imsi,
+ ?'DIAMETER_CX_SERVER-ASSIGNMENT-TYPE_REGISTRATION',
+ Apn) of
+ ok -> Data1 = Data#ue_fsm_data{s6b_resp_pid = Pid, apn = Apn},
{next_state, state_authenticated_wait_swx_saa, Data1, [{reply,From,ok}]};
{error, Err} -> {keep_state, Data, [{reply,From,{error, Err}}]}
end;

+state_authenticated({call, From}, rx_swm_str, Data) ->
+ lager:info("ue_fsm state_authenticated event=rx_swm_str, ~p~n", [Data]),
+ case {Data#ue_fsm_data.epdg_sess_active, Data#ue_fsm_data.pgw_sess_active} of
+ {false, _} -> %% The SWm session is not active...
+ 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", []),
+ 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
+ case aaa_diameter_swx:server_assignment_request(Data#ue_fsm_data.imsi,
+ ?'DIAMETER_CX_SERVER-ASSIGNMENT-TYPE_USER_DEREGISTRATION',
+ Data#ue_fsm_data.apn) of
+ ok -> {next_state, state_authenticated_wait_swx_saa, Data, [{reply,From,ok}]};
+ {error, _Err} ->
+ DiaRC = 5002, %% UNKNOWN_SESSION_ID
+ {keep_state, Data, [{reply,From,{error, DiaRC}}]}
+ end
+ end;
+
+state_authenticated({call, {Pid, _Tag} = From}, rx_s6b_str, Data) ->
+ lager:info("ue_fsm state_authenticated event=rx_s6b_str, ~p~n", [Data]),
+ case {Data#ue_fsm_data.pgw_sess_active, Data#ue_fsm_data.epdg_sess_active} of
+ {false, _} -> %% The S6b session is not active...
+ 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_s6b_str: ePDG session still active, skip updating the HSS~n", []),
+ Data1 = Data#ue_fsm_data{pgw_sess_active = false},
+ {keep_state, Data1, [{reply,From,{ok, 2001}}]};
+ {true, false} -> %% All sessions will now be gone, trigger SAR Type=USER_DEREGISTRATION
+ case aaa_diameter_swx:server_assignment_request(Data#ue_fsm_data.imsi,
+ ?'DIAMETER_CX_SERVER-ASSIGNMENT-TYPE_USER_DEREGISTRATION',
+ Data#ue_fsm_data.apn) of
+ ok -> Data1 = Data#ue_fsm_data{s6b_resp_pid = Pid},
+ {next_state, state_authenticated_wait_swx_saa, Data1, [{reply,From,ok}]};
+ {error, _Err} ->
+ DiaRC = 5002, %% UNKNOWN_SESSION_ID
+ {keep_state, Data, [{reply,From,{error, DiaRC}}]}
+ end
+ end;
+
state_authenticated({call, From}, _Whatever, Data) ->
lager:info("ue_fsm state_authenticated event=purge_ms_request, ~p~n", [Data]),
{keep_state, Data, [{reply,From,ok}]}.
@@ -177,9 +243,22 @@
state_authenticated_wait_swx_saa(enter, _OldState, Data) ->
{keep_state, Data};

-state_authenticated_wait_swx_saa({call, From}, {rx_swx_saa, ResultCode}, Data) ->
- lager:info("ue_fsm state_authenticated_wait_swx_saa event=rx_swx_saa ResulCode=~p, ~p~n", [ResultCode, Data]),
- aaa_diameter_s6b:tx_aa_answer(Data#ue_fsm_data.s6b_resp_pid, ResultCode),
- Data1 = Data#ue_fsm_data{s6b_resp_pid = undefined},
- {next_state, state_authenticated, Data1, [{reply,From,ok}]}.
-
+state_authenticated_wait_swx_saa({call, From}, {rx_swx_saa, SAType, ResultCode}, Data) ->
+ lager:info("ue_fsm state_authenticated_wait_swx_saa event=rx_swx_saa SAType=~p ResulCode=~p, ~p~n", [SAType, ResultCode, Data]),
+ case SAType of
+ ?'DIAMETER_CX_SERVER-ASSIGNMENT-TYPE_REGISTRATION' ->
+ aaa_diameter_s6b:tx_aa_answer(Data#ue_fsm_data.s6b_resp_pid, ResultCode),
+ Data1 = Data#ue_fsm_data{pgw_sess_active = true, s6b_resp_pid = undefined},
+ {next_state, state_authenticated, Data1, [{reply,From,ok}]};
+ ?'DIAMETER_CX_SERVER-ASSIGNMENT-TYPE_USER_DEREGISTRATION' ->
+ case Data#ue_fsm_data.s6b_resp_pid of
+ undefined -> %% SWm initiated
+ aaa_diameter_swm:session_termination_answer(Data#ue_fsm_data.imsi, ResultCode),
+ Data1 = Data#ue_fsm_data{epdg_sess_active = false},
+ {next_state, state_new, Data1, [{reply,From,ok}]};
+ _ -> %% S6b initiated
+ aaa_diameter_s6b:tx_st_answer(Data#ue_fsm_data.s6b_resp_pid, ResultCode),
+ 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

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

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