fixeria has uploaded this change for review.

View Change

erab_fsm: implement E-RAB MODIFY Ind/Cnf procedure

The eNB-initiated E-RAB modification procedure is defined
in 3GPP TS 36.413 section 8.2.4. This is pretty much the same
as the MME-initiated E-RAB modification.

Change-Id: I750ada0a5a21edc8bc06d567c8000b6304966474
---
M src/erab_fsm.erl
M test/erab_fsm_test.erl
M test/pfcp_mock.hrl
3 files changed, 119 insertions(+), 3 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/47/39147/1
diff --git a/src/erab_fsm.erl b/src/erab_fsm.erl
index c684510..9d11be6 100644
--- a/src/erab_fsm.erl
+++ b/src/erab_fsm.erl
@@ -51,6 +51,8 @@
erab_setup_rsp/2,
erab_modify_req/2,
erab_modify_rsp/2,
+ erab_modify_ind/2,
+ erab_modify_cnf/2,
erab_release/2,
erab_release_cmd/1,
erab_release_rsp/1,
@@ -77,7 +79,9 @@

-type mod_kind() :: setup_rsp |
modify_req |
- modify_rsp_nack.
+ modify_rsp_nack |
+ modify_ind |
+ modify_cnf_nack.
-type rel_kind() :: cmd | ind.

-record(erab_state, {uid :: term(), %% unique E-RAB identifier
@@ -87,6 +91,7 @@
a2u :: undefined | teid_addr(), %% GTP-U params for UPF <- Access
u2a :: undefined | teid_addr(), %% GTP-U params for UPF -> Access
u2cm :: undefined | teid_addr(), %% GTP-U params for UPF -> Core (modified)
+ u2am :: undefined | teid_addr(), %% GTP-U params for UPF -> Access (modified)
seid_loc :: undefined | pfcp_peer:pfcp_seid(), %% local SEID, chosen by us
seid_rem :: undefined | pfcp_peer:pfcp_seid(), %% remote SEID, chosen by the UPF
mod_kind :: undefined | mod_kind(), %% E-RAB MODIFY kind
@@ -156,6 +161,28 @@
gen_statem:call(Pid, {?FUNCTION_NAME, Res}).


+%% @doc Indicate reception of E-RAB modify indication (from access).
+%% @param Pid PID of an erab_fsm.
+%% @param F_TEID TEID and bind address indicated by the eNB.
+%% @returns TEID and Addr to be sent to the MME; an error otherwise.
+%% @end
+-spec erab_modify_ind(pid(), teid_addr()) -> {ok, teid_addr()} |
+ {error, term()}.
+erab_modify_ind(Pid, F_TEID) ->
+ gen_statem:call(Pid, {?FUNCTION_NAME, F_TEID}).
+
+
+%% @doc Indicate reception of E-RAB modify confirmation (from core).
+%% @param Pid PID of an erab_fsm.
+%% @param Res ack if MME indicates successful modification;
+%% nack if MME indicates unsuccessful modification.
+%% @returns ok or an error.
+%% @end
+-spec erab_modify_cnf(pid(), ack | nack) -> ok | {error, term()}.
+erab_modify_cnf(Pid, Res) ->
+ gen_statem:call(Pid, {?FUNCTION_NAME, Res}).
+
+
-spec erab_release(pid(), rel_kind()) -> ok.
erab_release(Pid, cmd) ->
erab_release_cmd(Pid);
@@ -320,6 +347,10 @@
modify_req ->
{ok, S#erab_state.a2u};
modify_rsp_nack ->
+ ok;
+ modify_ind ->
+ {ok, S#erab_state.c2u};
+ modify_cnf_nack ->
ok
end,
{next_state, erab_setup,
@@ -379,6 +410,41 @@
end;

erab_setup({call, From},
+ {erab_modify_ind, U2AM},
+ #erab_state{} = S) ->
+ ?LOG_DEBUG("Rx E-RAB MODIFY Ind (U2AM ~p)", [U2AM]),
+ {next_state, session_modify,
+ S#erab_state{mod_kind = modify_ind,
+ from = From,
+ u2am = U2AM}};
+
+erab_setup({call, From},
+ {erab_modify_cnf, Res},
+ #erab_state{u2am = undefined}) ->
+ %% Ignore erab_modify_cnf events without prior erab_modify_ind. This happens
+ %% when the S1AP E-RAB MODIFY Ind contains no F-TEID (nothing to modify).
+ ?LOG_DEBUG("Rx E-RAB MODIFY Cnf (~p), F-TEID remains unchanged", [Res]),
+ {keep_state_and_data, {reply, From, ok}};
+
+erab_setup({call, From},
+ {erab_modify_cnf, Res},
+ #erab_state{u2am = U2AM} = S0) ->
+ ?LOG_DEBUG("Rx E-RAB MODIFY Cnf (~p)", [Res]),
+ case Res of
+ ack ->
+ {keep_state,
+ S0#erab_state{u2a = U2AM,
+ u2am = undefined},
+ {reply, From, ok}};
+ nack ->
+ %% revert PFCP session params
+ {next_state, session_modify,
+ S0#erab_state{mod_kind = modify_cnf_nack,
+ u2am = undefined,
+ from = From}}
+ end;
+
+erab_setup({call, From},
erab_release_cmd,
#erab_state{} = S) ->
?LOG_DEBUG("Rx E-RAB RELEASE Cmd"),
@@ -559,7 +625,21 @@
u2c = U2C}) ->
%% E-RAB MODIFY Rsp (NACK) - eNB NACK's E-RAB modification
%% so we modify the respective FAR (id=2)
- session_modify_req(SEID, 2, U2C).
+ session_modify_req(SEID, 2, U2C);
+
+session_modify_req(#erab_state{mod_kind = modify_ind,
+ seid_rem = SEID,
+ u2am = U2AM}) ->
+ %% E-RAB MODIFY Ind - eNB orders modification of U2A F-TEID,
+ %% so we modify the respective FAR (id=1)
+ session_modify_req(SEID, 1, U2AM);
+
+session_modify_req(#erab_state{mod_kind = modify_cnf_nack,
+ seid_rem = SEID,
+ u2a = U2A}) ->
+ %% E-RAB MODIFY Cnf (NACK) - MME NACK's E-RAB modification
+ %% so we modify the respective FAR (id=1)
+ session_modify_req(SEID, 1, U2A).


-spec session_modify_req(SEID, FAR_ID, F_TEID) -> Result
diff --git a/test/erab_fsm_test.erl b/test/erab_fsm_test.erl
index 5563d02..c000701 100644
--- a/test/erab_fsm_test.erl
+++ b/test/erab_fsm_test.erl
@@ -9,6 +9,7 @@
-define(U2A, {?TEID_U2A, ?ADDR_U2A}).

-define(U2CM, {?TEID_U2CM, ?ADDR_U2CM}).
+-define(U2AM, {?TEID_U2AM, ?ADDR_U2AM}).

%% ------------------------------------------------------------------
%% setup/misc functions
@@ -54,7 +55,11 @@
[{"E-RAB MODIFY REQ :: ACK",
?TC(fun test_erab_modify_req_ack/1)},
{"E-RAB MODIFY REQ :: NACK",
- ?TC(fun test_erab_modify_req_nack/1)}].
+ ?TC(fun test_erab_modify_req_nack/1)},
+ {"E-RAB MODIFY IND :: ACK",
+ ?TC(fun test_erab_modify_ind_ack/1)},
+ {"E-RAB MODIFY IND :: NACK",
+ ?TC(fun test_erab_modify_ind_nack/1)}].


erab_release_test_() ->
@@ -143,6 +148,35 @@
test_erab_modify_req(Pid, nack).


+test_erab_modify_ind(Pid, Res) ->
+ U2A = case Res of
+ ack -> ?U2AM;
+ nack -> ?U2A
+ end,
+ [?_assertEqual({ok, ?A2U}, erab_fsm:erab_setup_req(Pid, ?U2C)),
+ ?_assertEqual({ok, ?C2U}, erab_fsm:erab_setup_rsp(Pid, ?U2A)),
+ %% E-RAB MODIFY Ind indicates the new (modified) F-TEID to be used
+ %% in the UPF -> Access direction. The FSM logic indicates the old
+ %% (unmodified) F-TEID to be used in the UPF <- Core direction.
+ ?_assertEqual({ok, ?C2U}, erab_fsm:erab_modify_ind(Pid, ?U2AM)),
+ %% Before getting the Rsp, the U2C F-TEID remains unchanged
+ ?_assertEqual(?U2A, erab_fsm_info(Pid, f_teid_u2a)),
+ %% After getting the Cnf (ACK), the new (modified) U2A F-TEID is used
+ %% After getting the Cnf (NACK), the U2A F-TEID remains unchanged
+ ?_assertEqual(ok, erab_fsm:erab_modify_cnf(Pid, Res)),
+ ?_assertEqual(U2A, erab_fsm_info(Pid, f_teid_u2a)),
+ ?_assertEqual(ok, erab_fsm:shutdown(Pid)),
+ ?_assertNot(erlang:is_process_alive(Pid))].
+
+
+test_erab_modify_ind_ack(Pid) ->
+ test_erab_modify_ind(Pid, ack).
+
+
+test_erab_modify_ind_nack(Pid) ->
+ test_erab_modify_ind(Pid, nack).
+
+
test_erab_release_cmd_success(Pid) ->
[?_assertEqual({ok, ?A2U}, erab_fsm:erab_setup_req(Pid, ?U2C)),
?_assertEqual({ok, ?C2U}, erab_fsm:erab_setup_rsp(Pid, ?U2A)),
diff --git a/test/pfcp_mock.hrl b/test/pfcp_mock.hrl
index 9a0232a..609d5f2 100644
--- a/test/pfcp_mock.hrl
+++ b/test/pfcp_mock.hrl
@@ -7,6 +7,7 @@
-define(TEID_U2A, 16#0002). %% UPF -> Access

-define(TEID_U2CM, 16#0f0001). %% UPF -> Core (modified)
+-define(TEID_U2AM, 16#0f0002). %% UPF -> Access (modified)

-define(ADDR_U2C, << 16#7f, 16#00, 16#00, 16#01 >>). %% UPF -> Core
-define(ADDR_C2U, << 16#7f, 16#00, 16#01, 16#01 >>). %% UPF <- Core
@@ -14,5 +15,6 @@
-define(ADDR_U2A, << 16#7f, 16#00, 16#00, 16#02 >>). %% UPF -> Access

-define(ADDR_U2CM, << 16#7f, 16#0f, 16#00, 16#01 >>). %% UPF -> Core (modified)
+-define(ADDR_U2AM, << 16#7f, 16#0f, 16#00, 16#02 >>). %% UPF -> Access (modified)

%% vim:set ts=4 sw=4 et:

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

Gerrit-MessageType: newchange
Gerrit-Project: erlang/osmo-s1gw
Gerrit-Branch: master
Gerrit-Change-Id: I750ada0a5a21edc8bc06d567c8000b6304966474
Gerrit-Change-Number: 39147
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de>