fixeria has uploaded this change for review.

View Change

erab_fsm: implement E-RAB MODIFY Req/Rsp procedures

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

git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/16/39116/1
diff --git a/src/erab_fsm.erl b/src/erab_fsm.erl
index a479cea..35df15f 100644
--- a/src/erab_fsm.erl
+++ b/src/erab_fsm.erl
@@ -49,6 +49,8 @@
-export([start_link/1,
erab_setup_req/2,
erab_setup_rsp/2,
+ erab_modify_req/2,
+ erab_modify_rsp/2,
erab_release/2,
erab_release_cmd/1,
erab_release_rsp/1,
@@ -73,6 +75,9 @@
addr() %% GTP-U Transport Layer Address
}.

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

-record(erab_state, {uid :: term(), %% unique E-RAB identifier
@@ -81,8 +86,10 @@
c2u :: undefined | teid_addr(), %% GTP-U params for UPF <- Core
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)
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
rel_kind :: undefined | rel_kind() %% E-RAB RELEASE kind
}).

@@ -127,6 +134,28 @@
gen_statem:call(Pid, {?FUNCTION_NAME, F_TEID}).


+%% @doc Indicate reception of E-RAB modify request (from core).
+%% @param Pid PID of an erab_fsm.
+%% @param F_TEID TEID and bind address indicated by the MME.
+%% @returns TEID and Addr to be sent to the eNB; an error otherwise.
+%% @end
+-spec erab_modify_req(pid(), teid_addr()) -> {ok, teid_addr()} |
+ {error, term()}.
+erab_modify_req(Pid, F_TEID) ->
+ gen_statem:call(Pid, {?FUNCTION_NAME, F_TEID}).
+
+
+%% @doc Indicate reception of E-RAB modify response (from access).
+%% @param Pid PID of an erab_fsm.
+%% @param Res ack if eNB indicates successful modification;
+%% nack if eNB indicates unsuccessful modification.
+%% @returns ok or an error.
+%% @end
+-spec erab_modify_rsp(pid(), ack | nack) -> ok | {error, term()}.
+erab_modify_rsp(Pid, Res) ->
+ gen_statem:call(Pid, {?FUNCTION_NAME, Res}).
+
+
-spec erab_release(pid(), rel_kind()) -> ok.
erab_release(Pid, cmd) ->
erab_release_cmd(Pid);
@@ -256,7 +285,8 @@
#erab_state{} = S) ->
?LOG_DEBUG("Rx E-RAB SETUP Rsp (U2A ~p)", [U2A]),
{next_state, session_modify,
- S#erab_state{from = From,
+ S#erab_state{mod_kind = setup_rsp,
+ from = From,
u2a = U2A}};

erab_wait_setup_rsp(Event, EventData, S) ->
@@ -264,7 +294,8 @@


%% state SESSION_MODIFY :: PFCP session modification
-session_modify(enter, OldState, S) ->
+session_modify(enter, OldState,
+ #erab_state{} = S) ->
?LOG_DEBUG("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]),
ok = session_modify_req(S),
{next_state, ?FUNCTION_NAME, S, %% loop transition to enable state_timeout
@@ -277,24 +308,32 @@
{shutdown, timeout},
{reply, From, {error, {timeout, ?FUNCTION_NAME}}}};

-session_modify(info, #pfcp{} = PDU,
+session_modify(info, #pfcp{type = session_modification_response,
+ seid = SEID_Rsp, %% matches F-SEID we sent in the ESTABLISH Req
+ ie = #{pfcp_cause := 'Request accepted'}},
#erab_state{from = From,
seid_loc = SEID_Rsp,
- c2u = C2U} = S) ->
- case PDU of
- #pfcp{type = session_modification_response,
- seid = SEID_Rsp, %% matches F-SEID we sent in the ESTABLISH Req
- ie = #{pfcp_cause := 'Request accepted'}} ->
- ?LOG_DEBUG("PFCP session modified"),
- {next_state, erab_setup,
- S#erab_state{from = undefined},
- [{reply, From, {ok, C2U}}]};
- _ ->
- ?LOG_ERROR("Rx unexpected PFCP PDU: ~p", [PDU]),
- {stop_and_reply,
- {shutdown, unexp_pdu},
- {reply, From, {error, {unexp_pdu, ?FUNCTION_NAME}}}}
- end;
+ mod_kind = ModKind} = S) ->
+ ?LOG_DEBUG("PFCP session modified"),
+ Reply = case ModKind of
+ setup_rsp ->
+ {ok, S#erab_state.c2u};
+ modify_req ->
+ {ok, S#erab_state.a2u};
+ modify_rsp_nack ->
+ ok
+ end,
+ {next_state, erab_setup,
+ S#erab_state{mod_kind = undefined,
+ from = undefined},
+ [{reply, From, Reply}]};
+
+session_modify(info, #pfcp{} = PDU,
+ #erab_state{from = From}) ->
+ ?LOG_ERROR("Rx unexpected PFCP PDU: ~p", [PDU]),
+ {stop_and_reply,
+ {shutdown, unexp_pdu},
+ {reply, From, {error, {unexp_pdu, ?FUNCTION_NAME}}}};

session_modify(Event, EventData, S) ->
handle_event(?FUNCTION_NAME, Event, EventData, S).
@@ -306,6 +345,33 @@
{keep_state, S};

erab_setup({call, From},
+ {erab_modify_req, U2CM},
+ #erab_state{} = S) ->
+ ?LOG_DEBUG("Rx E-RAB MODIFY Req (U2CM ~p)", [U2CM]),
+ {next_state, session_modify,
+ S#erab_state{mod_kind = modify_req,
+ from = From,
+ u2cm = U2CM}};
+
+erab_setup({call, From},
+ {erab_modify_rsp, Res},
+ #erab_state{u2cm = U2CM} = S0) ->
+ ?LOG_DEBUG("Rx E-RAB MODIFY Rsp (~p)", [Res]),
+ case Res of
+ ack ->
+ {keep_state,
+ S0#erab_state{u2c = U2CM,
+ u2cm = undefined},
+ {reply, From, ok}};
+ nack ->
+ %% revert PFCP session params
+ {next_state, session_modify,
+ S0#erab_state{mod_kind = modify_rsp_nack,
+ u2cm = undefined,
+ from = From}}
+ end;
+
+erab_setup({call, From},
erab_release_cmd,
#erab_state{} = S) ->
?LOG_DEBUG("Rx E-RAB RELEASE Cmd"),
@@ -466,19 +532,49 @@


-spec session_modify_req(erab_state()) -> pfcp_peer:pfcp_session_rsp().
-session_modify_req(#erab_state{seid_rem = SEID, %% SEID allocated to us
+session_modify_req(#erab_state{mod_kind = setup_rsp,
+ seid_rem = SEID,
u2a = U2A}) ->
+ %% Now we know the Access side TEID / GTP-U address, so we modify
+ %% the FAR1 (which was previously set to DROP) to FORW.
+ session_modify_req(SEID, 1, U2A);
+
+session_modify_req(#erab_state{mod_kind = modify_req,
+ seid_rem = SEID,
+ u2cm = U2CM}) ->
+ %% E-RAB MODIFY Req - MME orders modification of U2C F-TEID,
+ %% so we modify the respective FAR (id=2)
+ session_modify_req(SEID, 2, U2CM);
+
+session_modify_req(#erab_state{mod_kind = modify_rsp_nack,
+ seid_rem = SEID,
+ 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).
+
+
+-spec session_modify_req(SEID, FAR_ID, F_TEID) -> Result
+ when SEID :: pfcp_peer:pfcp_seid(),
+ FAR_ID :: integer(),
+ F_TEID :: teid_addr(),
+ Result :: pfcp_peer:pfcp_session_rsp().
+session_modify_req(SEID, FAR_ID, F_TEID) ->
%% Forwarding Action Rules
- FPars = #{outer_header_creation => ohc(U2A)},
- FParsNI = add_net_inst(FPars, 'Access'), %% optional Network Instance IE
- FARs = [#{far_id => {far_id, 1}, %% -- for Core -> Access
- %% Now we know the Access side TEID / GTP-U address, so we modify
- %% this FAR (which was previously set to DROP) to FORW.
+ Iface = far_id2iface(FAR_ID),
+ FPars = #{outer_header_creation => ohc(F_TEID)},
+ FParsNI = add_net_inst(FPars, Iface), %% optional Network Instance IE
+ FARs = [#{far_id => {far_id, FAR_ID},
apply_action => #{'FORW' => []},
update_forwarding_parameters => FParsNI}],
pfcp_peer:session_modify_req(SEID, [], FARs).


+-spec far_id2iface(integer()) -> iface().
+far_id2iface(1) -> 'Access';
+far_id2iface(2) -> 'Core'.
+
+
%% Network Instance IE env parameter name
-spec net_inst_param(iface()) -> atom().
net_inst_param('Core') -> pfcp_net_inst_core;
diff --git a/test/erab_fsm_test.erl b/test/erab_fsm_test.erl
index e406e88..5563d02 100644
--- a/test/erab_fsm_test.erl
+++ b/test/erab_fsm_test.erl
@@ -8,6 +8,8 @@
-define(A2U, {?TEID_A2U, ?ADDR_A2U}).
-define(U2A, {?TEID_U2A, ?ADDR_U2A}).

+-define(U2CM, {?TEID_U2CM, ?ADDR_U2CM}).
+
%% ------------------------------------------------------------------
%% setup/misc functions
%% ------------------------------------------------------------------
@@ -48,6 +50,13 @@
?TC(fun test_erab_setup_pfcp_modify_error/1)}].


+erab_modify_test_() ->
+ [{"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)}].
+
+
erab_release_test_() ->
[{"E-RAB RELEASE CMD :: success",
?TC(fun test_erab_release_cmd_success/1)},
@@ -105,6 +114,35 @@
?_assertNot(erlang:is_process_alive(Pid))].


+test_erab_modify_req(Pid, Res) ->
+ U2C = case Res of
+ ack -> ?U2CM;
+ nack -> ?U2C
+ end,
+ [?_assertEqual({ok, ?A2U}, erab_fsm:erab_setup_req(Pid, ?U2C)),
+ ?_assertEqual({ok, ?C2U}, erab_fsm:erab_setup_rsp(Pid, ?U2A)),
+ %% E-RAB MODIFY Req indicates the new (modified) F-TEID to be used
+ %% in the UPF -> Core direction. The FSM logic indicates the old
+ %% (unmodified) F-TEID to be used in the UPF <- Access direction.
+ ?_assertEqual({ok, ?A2U}, erab_fsm:erab_modify_req(Pid, ?U2CM)),
+ %% Before getting the Rsp, the U2C F-TEID remains unchanged
+ ?_assertEqual(?U2C, erab_fsm_info(Pid, f_teid_u2c)),
+ %% After getting the Rsp (ACK), the new (modified) U2C F-TEID is used
+ %% After getting the Rsp (NACK), the U2C F-TEID remains unchanged
+ ?_assertEqual(ok, erab_fsm:erab_modify_rsp(Pid, Res)),
+ ?_assertEqual(U2C, erab_fsm_info(Pid, f_teid_u2c)),
+ ?_assertEqual(ok, erab_fsm:shutdown(Pid)),
+ ?_assertNot(erlang:is_process_alive(Pid))].
+
+
+test_erab_modify_req_ack(Pid) ->
+ test_erab_modify_req(Pid, ack).
+
+
+test_erab_modify_req_nack(Pid) ->
+ test_erab_modify_req(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 c044939..9a0232a 100644
--- a/test/pfcp_mock.hrl
+++ b/test/pfcp_mock.hrl
@@ -6,9 +6,13 @@
-define(TEID_A2U, 16#0202). %% UPF <- Access
-define(TEID_U2A, 16#0002). %% UPF -> Access

+-define(TEID_U2CM, 16#0f0001). %% UPF -> Core (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
-define(ADDR_A2U, << 16#7f, 16#00, 16#02, 16#02 >>). %% UPF <- Access
-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)
+
%% vim:set ts=4 sw=4 et:

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

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