Compare commits
4 Commits
ce2299d176
...
7bf413c2a0
Author | SHA1 | Date |
---|---|---|
Pau Espin | 7bf413c2a0 | |
Pau Espin | ff57ae27ff | |
Pau Espin | 859807eb7f | |
Pau Espin | 0d09d1e568 |
|
@ -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"},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
rebar3 compile
|
||||
rebar3 escriptize
|
||||
rebar3 eunit
|
||||
make clean || true
|
||||
make
|
||||
make check
|
||||
|
|
|
@ -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"}}}
|
||||
|
|
|
@ -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.
|
|
@ -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}.
|
|
@ -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}.
|
||||
|
|
|
@ -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], []) ->
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
%% ------------------------------------------------------------------
|
||||
|
|
|
@ -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}.
|
||||
|
|
Loading…
Reference in New Issue