Skip to content

Commit

Permalink
rework context teardown on inactivity to include a proper cause
Browse files Browse the repository at this point in the history
Rework the context inactivity handling to generate a delete GTP
message with a related cause (PDP address inactivity timer expire /
PDN connection inactivity timer expire).
  • Loading branch information
RoadRunnr authored and javiermtorres committed Sep 14, 2021
1 parent b017fa9 commit e41a4e4
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 27 deletions.
13 changes: 11 additions & 2 deletions apps/ergw_core/src/ggsn_gn.erl
Original file line number Diff line number Diff line change
Expand Up @@ -852,12 +852,21 @@ send_context_alive_request(#{left_tunnel := Tunnel, context :=
RequestIEs = gtp_v1_c:build_recovery(Type, Tunnel, false, RequestIEs0),
send_request(Tunnel, ?T3, ?N3, Type, RequestIEs, alive_check).

map_term_cause(TermCause)
when TermCause =:= cp_inactivity_timeout;
TermCause =:= up_inactivity_timeout ->
pdp_address_inactivity_timer_expires;
map_term_cause(_TermCause) ->
reactivation_requested.

delete_context(From, TermCause, connected,
#{left_tunnel := Tunnel, context :=
#context{default_bearer_id = NSAPI}} = Data) ->
Type = delete_pdp_context_request,
RequestIEs0 = [#nsapi{nsapi = NSAPI},
#teardown_ind{value = 1}],
RequestIEs0 =
[#cause{value = map_term_cause(TermCause)},
#nsapi{nsapi = NSAPI},
#teardown_ind{value = 1}],
RequestIEs = gtp_v1_c:build_recovery(Type, Tunnel, false, RequestIEs0),
send_request(Tunnel, ?T3, ?N3, Type, RequestIEs, {From, TermCause}),
{next_state, shutdown_initiated, Data};
Expand Down
6 changes: 3 additions & 3 deletions apps/ergw_core/src/gtp_context.erl
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,9 @@ handle_event({call, From},
handle_event({call, From},
{sx, #pfcp{type = session_report_request,
ie = #{report_type := #report_type{upir = 1}}}},
State, #{pfcp := PCtx} = Data0) ->
Data = close_context(both, up_inactivity_timeout, State, Data0),
{next_state, shutdown, Data, [{reply, From, {ok, PCtx}}]};
State, #{pfcp := PCtx} = Data) ->
gen_statem:reply(From, {ok, PCtx}),
delete_context(undefined, up_inactivity_timeout, State, Data);

%% Usage Report
handle_event({call, From},
Expand Down
9 changes: 8 additions & 1 deletion apps/ergw_core/src/pgw_s5s8.erl
Original file line number Diff line number Diff line change
Expand Up @@ -918,11 +918,18 @@ send_request(Tunnel, T3, N3, Type, RequestIEs, ReqInfo) ->
send_request(Tunnel, Src, DstIP, DstPort, T3, N3, Msg, ReqInfo) ->
gtp_context:send_request(Tunnel, Src, DstIP, DstPort, T3, N3, Msg, ReqInfo).

map_term_cause(TermCause)
when TermCause =:= cp_inactivity_timeout;
TermCause =:= up_inactivity_timeout ->
pdn_connection_inactivity_timer_expires;
map_term_cause(_TermCause) ->
reactivation_requested.

delete_context(From, TermCause, connected,
#{left_tunnel := Tunnel, context :=
#context{default_bearer_id = EBI}} = Data) ->
Type = delete_bearer_request,
RequestIEs0 = [#v2_cause{v2_cause = reactivation_requested},
RequestIEs0 = [#v2_cause{v2_cause = map_term_cause(TermCause)},
#v2_eps_bearer_id{eps_bearer_id = EBI}],
RequestIEs = gtp_v2_c:build_recovery(Type, Tunnel, false, RequestIEs0),
send_request(Tunnel, ?T3, ?N3, Type, RequestIEs, {From, TermCause}),
Expand Down
9 changes: 8 additions & 1 deletion apps/ergw_core/src/saegw_s11.erl
Original file line number Diff line number Diff line change
Expand Up @@ -743,10 +743,17 @@ send_request(Tunnel, T3, N3, Type, RequestIEs, ReqInfo) ->
send_request(Tunnel, Src, DstIP, DstPort, T3, N3, Msg, ReqInfo) ->
gtp_context:send_request(Tunnel, Src, DstIP, DstPort, T3, N3, Msg, ReqInfo).

map_term_cause(TermCause)
when TermCause =:= cp_inactivity_timeout;
TermCause =:= up_inactivity_timeout ->
pdn_connection_inactivity_timer_expires;
map_term_cause(_TermCause) ->
reactivation_requested.

delete_context(From, TermCause, connected, #{left_tunnel := Tunnel} = Data) ->
Type = delete_bearer_request,
EBI = 5,
RequestIEs0 = [#v2_cause{v2_cause = reactivation_requested},
RequestIEs0 = [#v2_cause{v2_cause = map_term_cause(TermCause)},
#v2_eps_bearer_id{eps_bearer_id = EBI}],
RequestIEs = gtp_v2_c:build_recovery(Type, Tunnel, false, RequestIEs0),
send_request(Tunnel, ?T3, ?N3, Type, RequestIEs, {From, TermCause}),
Expand Down
70 changes: 64 additions & 6 deletions apps/ergw_core/test/ggsn_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ common() ->
gx_invalid_charging_rulebase,
gx_invalid_charging_rule,
gx_rar_gy_interaction,
gtp_idle_timeout,
gtp_idle_timeout_gtp_session_loss,
gtp_idle_timeout_pfcp_session_loss,
up_inactivity_timer,
pfcp_sx_association_metric].

Expand Down Expand Up @@ -658,7 +659,9 @@ init_per_testcase(gx_invalid_charging_rule, Config) ->
ergw_test_lib:load_aaa_answer_config([{{gx, 'CCR-Initial'}, 'Initial-Gx-Fail-2'}]),
Config;
%% gtp 'inactivity_timeout' reduced to 300ms for test purposes
init_per_testcase(gtp_idle_timeout, Config) ->
init_per_testcase(TestCase, Config)
when TestCase =:= gtp_idle_timeout_gtp_session_loss;
TestCase =:= gtp_idle_timeout_pfcp_session_loss ->
ergw_test_lib:set_apn_key('inactivity_timeout', 300),
ok = meck:expect(ergw_gtp_c_socket, send_request,
fun(Socket, Src, DstIP, DstPort, _T3, _N3,
Expand Down Expand Up @@ -724,7 +727,9 @@ end_per_testcase(create_pdp_context_overload, Config) ->
jobs:modify_regulator(rate, create, {rate,create,1}, [{limit,100}]),
end_per_testcase(Config);
%% gtp 'inactivity_timeout' reset to default 28800000ms ~8 hrs
end_per_testcase(gtp_idle_timeout, Config) ->
end_per_testcase(TestCase, Config)
when TestCase =:= gtp_idle_timeout_gtp_session_loss;
TestCase =:= gtp_idle_timeout_pfcp_session_loss ->
ergw_test_lib:set_apn_key('inactivity_timeout', 28800000),
ok = meck:delete(ergw_gtp_c_socket, send_request, 8),
end_per_testcase(Config);
Expand Down Expand Up @@ -2682,9 +2687,9 @@ gx_invalid_charging_rule(Config) ->
ok.

%%--------------------------------------------------------------------
gtp_idle_timeout() ->
gtp_idle_timeout_gtp_session_loss() ->
[{doc, "Checks if the gtp idle timeout is triggered"}].
gtp_idle_timeout(Config) ->
gtp_idle_timeout_gtp_session_loss(Config) ->
Cntl = whereis(gtpc_client_server),
{GtpC, _, _} = create_pdp_context(Config),
%% The meck wait timeout (400 ms) has to be more than then the inactivity_timeout
Expand All @@ -2704,6 +2709,49 @@ gtp_idle_timeout(Config) ->
Resp2 = make_response(Req2, invalid_teid, GtpC),
send_pdu(Cntl, GtpC, Resp2),

%% make sure we are not getting a Delete PDP Context Request
?equal(timeout, recv_pdu(GtpC, undefined, 200, fun(Why) -> Why end)),

delete_pdp_context(not_found, GtpC),

?equal([], outstanding_requests()),
ok = meck:wait(?HUT, terminate, '_', ?TIMEOUT),
wait4contexts(?TIMEOUT),

meck_validate(Config),
ok.

%%--------------------------------------------------------------------
gtp_idle_timeout_pfcp_session_loss() ->
[{doc, "Checks if the gtp idle timeout is triggered"}].
gtp_idle_timeout_pfcp_session_loss(Config) ->
Cntl = whereis(gtpc_client_server),

{GtpC, _, _} = create_pdp_context(Config),
%% The meck wait timeout (400 ms) has to be more than then the inactivity_timeout
ok = meck:wait(gtp_context, handle_event,
[{timeout, context_idle}, '_', '_', '_'], 400),

%% Timeout triggers a update_pdp_context_request towards the SGSN
Req1 = recv_pdu(Cntl, 5000),
?match(#gtp{type = update_pdp_context_request}, Req1),
Resp1 = make_response(Req1, simple, GtpC),
send_pdu(Cntl, GtpC, Resp1),

%% kill the UP session
ergw_test_sx_up:reset('pgw-u01'),

%% wait for session cleanup
Req = recv_pdu(Cntl, 5000),
?match(#gtp{type = delete_pdp_context_request,
ie = #{{cause,0} := #cause{value = pdp_address_inactivity_timer_expires}}},
Req),
Resp = make_response(Req, simple, GtpC),
send_pdu(Cntl, GtpC, Resp),

ct:sleep(100),
delete_pdp_context(not_found, GtpC),

?equal([], outstanding_requests()),
ok = meck:wait(?HUT, terminate, '_', ?TIMEOUT),
wait4contexts(?TIMEOUT),
Expand All @@ -2715,6 +2763,7 @@ gtp_idle_timeout(Config) ->
up_inactivity_timer() ->
[{doc, "Test expiry of the User Plane Inactivity Timer"}].
up_inactivity_timer(Config) ->
Cntl = whereis(gtpc_client_server),
CtxKey = #context_key{socket = 'irx', id = {imsi, ?'IMSI', 5}},
Interim = rand:uniform(1800) + 1800,
AAAReply = #{'Idle-Timeout' => 1800, 'Acct-Interim-Interval' => Interim},
Expand All @@ -2732,7 +2781,7 @@ up_inactivity_timer(Config) ->
meck:passthrough([Session, SessionOpts, Procedure, Opts])
end),

create_pdp_context(Config),
{GtpC, _, _} = create_pdp_context(Config),

{_Handler, Server} = gtp_context_reg:lookup(CtxKey),
true = is_pid(Server),
Expand All @@ -2747,6 +2796,15 @@ up_inactivity_timer(Config) ->

ergw_test_sx_up:up_inactivity_timer_expiry('pgw-u01', PCtx),

%% wait for session cleanup
Req = recv_pdu(Cntl, 5000),
?match(#gtp{type = delete_pdp_context_request}, Req),
Resp = make_response(Req, simple, GtpC),
send_pdu(Cntl, GtpC, Resp),

ct:sleep(100),
delete_pdp_context(not_found, GtpC),

?equal([], outstanding_requests()),
ok = meck:wait(?HUT, terminate, '_', ?TIMEOUT),
wait4contexts(?TIMEOUT),
Expand Down
39 changes: 32 additions & 7 deletions apps/ergw_core/test/pgw_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ common() ->
gx_invalid_charging_rule,
gx_rar_gy_interaction,
tdf_app_id,
gtp_idle_timeout,
gtp_idle_timeout_pfcp_session_loss,
up_inactivity_timer].

sx_fail() ->
Expand Down Expand Up @@ -1006,7 +1006,7 @@ init_per_testcase(tdf_app_id, Config) ->
ergw_test_lib:load_aaa_answer_config([{{gx, 'CCR-Initial'}, 'Initial-Gx-TDF-App'}]),
Config;
%% gtp inactivity_timeout reduced to 300ms for test purposes
init_per_testcase(gtp_idle_timeout, Config) ->
init_per_testcase(gtp_idle_timeout_pfcp_session_loss, Config) ->
ergw_test_lib:set_apn_key(inactivity_timeout, 300),
setup_per_testcase(Config),
Config;
Expand Down Expand Up @@ -1111,7 +1111,7 @@ end_per_testcase(TestCase, Config)
ergw_test_sx_up:nat_port_blocks('pgw-u01', example, []),
end_per_testcase(Config);
%% gtp inactivity_timeout reset to default 28800000ms ~8 hrs
end_per_testcase(gtp_idle_timeout, Config) ->
end_per_testcase(gtp_idle_timeout_pfcp_session_loss, Config) ->
ergw_test_lib:set_apn_key(inactivity_timeout, 28800000),
end_per_testcase(Config);
end_per_testcase(_, Config) ->
Expand Down Expand Up @@ -5643,15 +5643,30 @@ gx_invalid_charging_rule(Config) ->
ok.

%%--------------------------------------------------------------------
gtp_idle_timeout() ->
gtp_idle_timeout_pfcp_session_loss() ->
[{doc, "Checks if the gtp idle timeout is triggered"}].
gtp_idle_timeout(Config) ->
gtp_idle_timeout_pfcp_session_loss(Config) ->
Cntl = whereis(gtpc_client_server),

{GtpC, _, _} = create_session(Config),
%% The meck wait timeout (400 ms) has to be more than then the Idle-Timeout
ok = meck:wait(gtp_context, handle_event,
[{timeout, context_idle}, '_', '_', '_'], 400),

delete_session(GtpC),
%% kill the UP session
ergw_test_sx_up:reset('pgw-u01'),

%% wait for session cleanup
Req = recv_pdu(Cntl, 5000),
?match(#gtp{type = delete_bearer_request,
ie = #{{v2_cause,0} :=
#v2_cause{v2_cause = pdn_connection_inactivity_timer_expires}}},
Req),
Resp = make_response(Req, simple, GtpC),
send_pdu(Cntl, GtpC, Resp),

ct:sleep(100),
delete_session(not_found, GtpC),

?equal([], outstanding_requests()),

Expand All @@ -5665,6 +5680,7 @@ gtp_idle_timeout(Config) ->
up_inactivity_timer() ->
[{doc, "Test expiry of the User Plane Inactivity Timer"}].
up_inactivity_timer(Config) ->
Cntl = whereis(gtpc_client_server),
CtxKey = #context_key{socket = 'irx-socket', id = {imsi, ?'IMSI', 5}},
Interim = rand:uniform(1800) + 1800,
AAAReply = #{'Idle-Timeout' => 1800, 'Acct-Interim-Interval' => Interim},
Expand All @@ -5682,7 +5698,7 @@ up_inactivity_timer(Config) ->
meck:passthrough([Session, SessionOpts, Procedure, Opts])
end),

create_session(Config),
{GtpC, _, _} = create_session(Config),

{_Handler, Server} = gtp_context_reg:lookup(CtxKey),
true = is_pid(Server),
Expand All @@ -5697,6 +5713,15 @@ up_inactivity_timer(Config) ->

ergw_test_sx_up:up_inactivity_timer_expiry('pgw-u01', PCtx),

%% wait for session cleanup
Req = recv_pdu(Cntl, 5000),
?match(#gtp{type = delete_bearer_request}, Req),
Resp = make_response(Req, simple, GtpC),
send_pdu(Cntl, GtpC, Resp),

ct:sleep(100),
delete_session(not_found, GtpC),

?equal([], outstanding_requests()),

ok = meck:wait(?HUT, terminate, '_', ?TIMEOUT),
Expand Down
39 changes: 32 additions & 7 deletions apps/ergw_core/test/saegw_s11_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ common() ->
gx_invalid_charging_rulebase,
gx_invalid_charging_rule,
gx_rar_gy_interaction,
gtp_idle_timeout,
gtp_idle_timeout_pfcp_session_loss,
up_inactivity_timer].

groups() ->
Expand Down Expand Up @@ -602,7 +602,7 @@ init_per_testcase(gx_invalid_charging_rule, Config) ->
ergw_test_lib:load_aaa_answer_config([{{gx, 'CCR-Initial'}, 'Initial-Gx-Fail-2'}]),
Config;
%% gtp inactivity_timeout reduced to 300ms for test purposes
init_per_testcase(gtp_idle_timeout, Config) ->
init_per_testcase(gtp_idle_timeout_pfcp_session_loss, Config) ->
ergw_test_lib:set_apn_key(inactivity_timeout, 300),
setup_per_testcase(Config),
Config;
Expand Down Expand Up @@ -652,7 +652,7 @@ end_per_testcase(create_session_overload, Config) ->
end_per_testcase(Config),
Config;
%% gtp inactivity_timeout reset to default 28800000ms ~8 hrs
end_per_testcase(gtp_idle_timeout, Config) ->
end_per_testcase(gtp_idle_timeout_pfcp_session_loss, Config) ->
ergw_test_lib:set_apn_key(inactivity_timeout, 28800000),
end_per_testcase(Config),
Config;
Expand Down Expand Up @@ -2162,15 +2162,30 @@ gx_invalid_charging_rule(Config) ->
ok.

%%--------------------------------------------------------------------
gtp_idle_timeout() ->
gtp_idle_timeout_pfcp_session_loss() ->
[{doc, "Checks if the gtp idle timeout is triggered"}].
gtp_idle_timeout(Config) ->
gtp_idle_timeout_pfcp_session_loss(Config) ->
Cntl = whereis(gtpc_client_server),

{GtpC, _, _} = create_session(Config),
%% The meck wait timeout (400 ms) has to be more than then the Idle-Timeout
ok = meck:wait(gtp_context, handle_event,
[{timeout, context_idle}, '_', '_', '_'], 400),

delete_session(GtpC),
%% kill the UP session
ergw_test_sx_up:reset('pgw-u01'),

%% wait for session cleanup
Req = recv_pdu(Cntl, 5000),
?match(#gtp{type = delete_bearer_request,
ie = #{{v2_cause,0} :=
#v2_cause{v2_cause = pdn_connection_inactivity_timer_expires}}},
Req),
Resp = make_response(Req, simple, GtpC),
send_pdu(Cntl, GtpC, Resp),

ct:sleep(100),
delete_session(not_found, GtpC),

?equal([], outstanding_requests()),

Expand All @@ -2184,6 +2199,7 @@ gtp_idle_timeout(Config) ->
up_inactivity_timer() ->
[{doc, "Test expiry of the User Plane Inactivity Timer"}].
up_inactivity_timer(Config) ->
Cntl = whereis(gtpc_client_server),
CtxKey = #context_key{socket = 'irx', id = {imsi, ?'IMSI', 5}},
Interim = rand:uniform(1800) + 1800,
AAAReply = #{'Idle-Timeout' => 1800, 'Acct-Interim-Interval' => Interim},
Expand All @@ -2201,7 +2217,7 @@ up_inactivity_timer(Config) ->
meck:passthrough([Session, SessionOpts, Procedure, Opts])
end),

create_session(Config),
{GtpC, _, _} = create_session(Config),

{_Handler, Server} = gtp_context_reg:lookup(CtxKey),
true = is_pid(Server),
Expand All @@ -2216,6 +2232,15 @@ up_inactivity_timer(Config) ->

ergw_test_sx_up:up_inactivity_timer_expiry('pgw-u01', PCtx),

%% wait for session cleanup
Req = recv_pdu(Cntl, 5000),
?match(#gtp{type = delete_bearer_request}, Req),
Resp = make_response(Req, simple, GtpC),
send_pdu(Cntl, GtpC, Resp),

ct:sleep(100),
delete_session(not_found, GtpC),

?equal([], outstanding_requests()),
ok = meck:wait(?HUT, terminate, '_', ?TIMEOUT),

Expand Down

0 comments on commit e41a4e4

Please sign in to comment.