fixeria has submitted this change. ( 
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/39116?usp=email )

Change subject: erab_fsm: implement E-RAB MODIFY Req/Rsp procedure
......................................................................

erab_fsm: implement E-RAB MODIFY Req/Rsp procedure

The MME-initiated E-RAB modification procedure is defined
in 3GPP TS 36.413 section 8.2.2.

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

Approvals:
  pespin: Looks good to me, but someone else must approve
  osmith: Looks good to me, but someone else must approve
  Jenkins Builder: Verified
  fixeria: Looks good to me, approved




diff --git a/src/erab_fsm.erl b/src/erab_fsm.erl
index dbd7ec1..6713388 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);
@@ -257,7 +286,8 @@
                     #erab_state{} = S) ->
     ?LOG_DEBUG("Rx E-RAB SETUP Rsp (U2A ~s)", [pp(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(cast, erab_release_cmd,
@@ -293,13 +323,22 @@
 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) ->
+               #erab_state{mod_kind = ModKind,
+                           from = From,
+                           seid_loc = SEID_Rsp} = 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{from = undefined},
-     [{reply, From, {ok, C2U}}]};
+     S#erab_state{mod_kind = undefined,
+                  from = undefined},
+     [{reply, From, Reply}]};

 session_modify(info, #pfcp{} = PDU,
                #erab_state{from = From}) ->
@@ -317,6 +356,41 @@
     ?LOG_DEBUG("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]),
     {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 = undefined}) ->
+    %% Ignore erab_modify_rsp events without prior erab_modify_req.  This 
happens
+    %% when the S1AP E-RAB MODIFY Req contains no F-TEID (nothing to modify).
+    ?LOG_DEBUG("Rx E-RAB MODIFY Rsp (~p), F-TEID remains unchanged", [Res]),
+    {keep_state_and_data, {reply, From, ok}};
+
+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(cast, erab_release_cmd,
            #erab_state{} = S) ->
     ?LOG_DEBUG("Rx E-RAB RELEASE Cmd"),
@@ -480,19 +554,50 @@


 -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}) ->
+    %% E-RAB SETUP Rsp - eNB indicates the access side F-TEID,
+    %% so we modify the respective FAR (id=1), 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 ea508dd..1a40b6d 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 (in state erab_setup) :: success",
       ?TC(fun test_erab_setup_release_cmd_success/1)},
@@ -111,6 +120,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 E-RAB RELEASE.{cmd,rsp} received in state erab_setup
 test_erab_setup_release_cmd_success(Pid) ->
     [?_assertEqual({ok, ?A2U}, erab_fsm:erab_setup_req(Pid, ?U2C)),
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 https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/39116?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: erlang/osmo-s1gw
Gerrit-Branch: master
Gerrit-Change-Id: I24d57ea390d71b640cb42046a8658e9d30670682
Gerrit-Change-Number: 39116
Gerrit-PatchSet: 4
Gerrit-Owner: fixeria <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <[email protected]>
Gerrit-Reviewer: osmith <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>

Reply via email to