Compare commits

...

4 Commits

Author SHA1 Message Date
Pau Espin 7bf413c2a0 WIP: trigger encode error
Run in erlang shell:
ip link del gtp0; rebar3 shell --config ./config/local.config

Then do:
eunit:test(aaa_diameter_s6b,[verbose]).

Change-Id: I9f83ec2c4b8e09d2e01000bfa695c41129021cb7
2024-03-13 21:21:04 +01:00
Pau Espin ff57ae27ff Implement RTR Deregistration-Reason PERMANENT_TERMINATION
Change-Id: I57f2e02dc4034b63c118e4a4139b2830e38a2138
2024-03-13 20:21:59 +01:00
Pau Espin 859807eb7f rebar.config: Update required OTP version 25.2.3
Since anyway we need debian12 for other reasons, like newer kernel, then
also require OTP from debian12 which is the one being tested and we want
to support. At the moment, debian12 OTP package version is 25.2.3.
I am personally building with Archlinux's package version 26.2.2.

Change-Id: I0ac7c93adbe88e1b738f1319cf1280c119ed59d1
2024-03-13 20:20:39 +01:00
Pau Espin 0d09d1e568 jenkins.sh: use available Makefile, clean before building
The project needs to go through clean procedure to clean up the
generated diameter files, so that they are generated from newer dia
files again.

Change-Id: Id3f2d71c184cf387fa178d5138d401686a8d0a63
2024-03-13 20:12:55 +01:00
11 changed files with 304 additions and 39 deletions

View File

@ -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"},

View File

@ -1,5 +1,5 @@
#!/bin/sh -ex
rebar3 compile
rebar3 escriptize
rebar3 eunit
make clean || true
make
make check

View File

@ -10,7 +10,7 @@
{osmo_gsup, {git, "https://gerrit.osmocom.org/erlang/osmo_gsup", {branch, "osmocom/epdg"}}}
]}.
{minimum_otp_vsn, "20.3"}.
{minimum_otp_vsn, "25.2.3"}.
{plugins, [
{rebar3_diameter_compiler,
{git, "https://github.com/carlosedp/rebar3_diameter_compiler.git", {tag, "0.8.0"}}}

View File

@ -40,6 +40,7 @@
-behaviour(gen_server).
-include_lib("eunit/include/eunit.hrl").
-include_lib("diameter/include/diameter_gen_base_rfc6733.hrl").
-include_lib("diameter_3gpp_ts29_273_s6b.hrl").
@ -49,6 +50,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 +70,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 +96,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 +118,18 @@ peer_down(_API, SvcName, {_PeerRef, _} = Peer) ->
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 +139,27 @@ tx_st_answer(Pid, DiaRC) ->
% 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 +218,21 @@ tmod(tcp) ->
tmod(sctp) ->
diameter_sctp.
diameter_call(Msg, State) ->
diameter:call(?SVC_NAME, ?APP_ALIAS, Msg, [{timeout, State#s6b_state.tx_timeout},
detach]).
%% TESTING:
reverse_test() ->
State = #s6b_state{tx_timeout = 10000},
Imsi = "262422638508077",
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),
ok = Ret.

View File

@ -29,23 +29,31 @@ pick_peer([Peer | _], _, _SvcName, _State) ->
{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(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) wh
handle_request(Packet, _SvcName, Peer) ->
lager:error("S6b Rx unexpected msg from ~p: ~p~n", [Peer, Packet]),
erlang:error({unexpected, ?MODULE, ?LINE}).
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}.

View File

@ -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 @@ auth_compl_response(Imsi, Result) ->
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 @@ auth_compl_request(Imsi, Apn) ->
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 @@ handle_cast({str, Imsi}, State) ->
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 @@ handle_call({sta, Imsi, DiaRC}, _From, State) ->
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}.

View File

@ -78,10 +78,18 @@ handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) wh
'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 @@ handle_answer(#diameter_packet{msg = Msg, errors = Errors}, _Request, _SvcName,
#'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 @@ handle_answer(#diameter_packet{msg = Msg, errors = Errors}, Request, _SvcName, P
#'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 @@ handle_error(Reason, _Request, _SvcName, _Peer, ExtraPars) ->
%% 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], []) ->

View File

@ -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 @@ ev_rx_swm_str(Pid) ->
{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 @@ ev_rx_swx_saa(Pid, Result) ->
{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 @@ ev_rx_s6b_aar(Pid, {Apn, AgentInfoOpt}) ->
{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 @@ state_authenticated({call, From}, rx_swm_str, Data) ->
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 @@ state_authenticated({call, _From}, {swm_auth_req, PdpTypeNr, Apn, EAP}, Data) ->
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 @@ state_authenticated_wait_swx_saa({call, From}, {rx_swx_saa, Result}, Data) ->
Data1 = Data#ue_fsm_data{pgw_sess_active = false, s6b_resp_pid = undefined},
{next_state, state_new, Data1, [{reply,From,ok}]}
end
end.
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}.

View File

@ -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 @@ cause_gtp2gsup(_) -> ?GSUP_CAUSE_PROTO_ERR_UNSPEC.
-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;

View File

@ -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 @@ session_termination_request(Imsi) ->
_ -> 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 @@ handle_call({str, Imsi}, _From, State) ->
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 @@ handle_cast({sta, Imsi, Result}, State) ->
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 @@ auth_compl_response(Imsi, Result) ->
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
%% ------------------------------------------------------------------

View File

@ -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 @@ received_swm_session_termination_answer(Pid, Result) ->
{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 @@ state_active({call, From}, received_gtpc_delete_bearer_request, Data) ->
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}, {received_swm_sta, DiaRC
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}.