fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/39116?usp=email )
Change subject: erab_fsm: implement E-RAB MODIFY Req/Rsp procedures ......................................................................
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: