fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/39113?usp=email )
Change subject: s1ap_proxy: handle_ies(): pass IEI path to handle_ie() ......................................................................
s1ap_proxy: handle_ies(): pass IEI path to handle_ie()
This patch prepares for a follow-up change adding E-RAB MODIFY REQ/RSP.
We need to be able to match the IEs unambiguously, because the same IEI may be used in different contexts and thus have different meaning, sometimes within the same PDU. The E-RABItem IE is such an example:
* 9.1.3.5 E-RAB RELEASE COMMAND ** contains an E-RABToBeReleasedList IE *** contains E-RABItem IEs
* 9.1.3.7 E-RAB RELEASE INDICATION ** contains an E-RABReleasedList IE *** contains E-RABItem IEs
So far we handled this duality by passing the release kind via the #proxy_state record ('rel_kind' field) and it worked. But soon in follow-up changes we'll have to deal with other PDUs:
* 9.1.3.4 E-RAB MODIFY RESPONSE ** contains an E-RABModifyListBearerModRes IE ** may contain an E-RABFailedToModifyList IE *** contains E-RABItem IEs
* 9.1.3.9 E-RAB MODIFICATION CONFIRM ** may contain an E-RABFailedToModifyList IE *** contains E-RABItem IEs ** may contain an E-RABToBeReleased IE *** contains E-RABItem IEs
A universal approach allowing to match and handle IEs properly depending on the context is to maintain the full IEI path in handle_ies() and pass it to handle_ie().
Change-Id: I35528a91f7dbf321ad1a0811f1bbcdfb529d3530 --- M src/s1ap_proxy.erl 1 file changed, 59 insertions(+), 38 deletions(-)
Approvals: laforge: Looks good to me, but someone else must approve pespin: Looks good to me, but someone else must approve fixeria: Looks good to me, approved Jenkins Builder: Verified
diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index b0d174d..18b054b 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -71,7 +71,7 @@ -record(proxy_state, {erabs :: dict:dict(), mme_ue_id :: undefined | mme_ue_id(), enb_ue_id :: undefined | enb_ue_id(), - rel_kind :: undefined | cmd | ind + path :: [s1ap_ie_id()] }).
-type proxy_state() :: #proxy_state{}. @@ -118,7 +118,8 @@
init([]) -> process_flag(trap_exit, true), - {ok, #proxy_state{erabs = dict:new()}}. + {ok, #proxy_state{erabs = dict:new(), + path = []}}.
handle_call({process_pdu, OrigData}, _From, @@ -275,8 +276,7 @@ ?LOG_DEBUG("Processing E-RAB RELEASE COMMAND"), s1gw_metrics:ctr_inc(?S1GW_CTR_S1AP_PROXY_IN_PKT_ERAB_RELEASE_CMD), case handle_ies(?'id-E-RABToBeReleasedList', - C0#'E-RABReleaseCommand'.protocolIEs, - S0#proxy_state{rel_kind = cmd}) of + C0#'E-RABReleaseCommand'.protocolIEs, S0) of {{ok, IEs}, S1} -> C1 = C0#'E-RABReleaseCommand'{protocolIEs = IEs}, PDU = {Outcome, Msg#'InitiatingMessage'{value = C1}}, @@ -312,8 +312,7 @@ ?LOG_DEBUG("Processing E-RAB RELEASE INDICATION"), s1gw_metrics:ctr_inc(?S1GW_CTR_S1AP_PROXY_IN_PKT_ERAB_RELEASE_IND), case handle_ies(?'id-E-RABReleasedList', - C0#'E-RABReleaseIndication'.protocolIEs, - S0#proxy_state{rel_kind = ind}) of + C0#'E-RABReleaseIndication'.protocolIEs, S0) of {{ok, IEs}, S1} -> C1 = C0#'E-RABReleaseIndication'{protocolIEs = IEs}, PDU = {Outcome, Msg#'InitiatingMessage'{value = C1}}, @@ -392,19 +391,20 @@
%% Handle a single IE (Information Element) -type handle_ie_result() :: {ok, s1ap_ie_val()} | {error, term()}. --spec handle_ie(IEI, C, S) -> Result - when IEI :: s1ap_ie_id(), - C :: s1ap_ie_val(), - S :: proxy_state(), +-spec handle_ie(Path, Content, State) -> Result + when Path :: [s1ap_ie_id()], + Content :: s1ap_ie_val(), + State :: proxy_state(), Result :: {handle_ie_result(), proxy_state()}.
%% E-RAB SETUP REQUEST related IEs -handle_ie(?'id-E-RABToBeSetupListBearerSUReq', C, S) -> +handle_ie([?'id-E-RABToBeSetupListBearerSUReq'], C, S) -> %% This IE contains a list of BearerSUReq, so patch inner IEs handle_ies(?'id-E-RABToBeSetupItemBearerSUReq', C, S);
-handle_ie(?'id-E-RABToBeSetupItemBearerSUReq', +handle_ie([?'id-E-RABToBeSetupItemBearerSUReq', + ?'id-E-RABToBeSetupListBearerSUReq'], #'E-RABToBeSetupItemBearerSUReq'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S0) -> @@ -420,11 +420,12 @@ end;
%% E-RAB SETUP RESPONSE related IEs -handle_ie(?'id-E-RABSetupListBearerSURes', C, S) -> +handle_ie([?'id-E-RABSetupListBearerSURes'], C, S) -> %% This IE contains a list of BearerSURes, so patch inner IEs handle_ies(?'id-E-RABSetupItemBearerSURes', C, S);
-handle_ie(?'id-E-RABSetupItemBearerSURes', +handle_ie([?'id-E-RABSetupItemBearerSURes', + ?'id-E-RABSetupListBearerSURes'], #'E-RABSetupItemBearerSURes'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> @@ -445,17 +446,17 @@ end;
%% 9.1.3.5 E-RAB RELEASE COMMAND related IEs -handle_ie(?'id-E-RABToBeReleasedList', C, S) -> +handle_ie([?'id-E-RABToBeReleasedList'], C, S) -> %% This IE contains a list of E-RABItem handle_ies(?'id-E-RABItem', C, S);
-handle_ie(?'id-E-RABItem', - #'E-RABItem'{'e-RAB-ID' = ERABId} = C, - #proxy_state{rel_kind = RelKind} = S) -> +handle_ie([?'id-E-RABItem', + ?'id-E-RABToBeReleasedList'], + #'E-RABItem'{'e-RAB-ID' = ERABId} = C, S) -> %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> - ok = erab_fsm:erab_release(Pid, RelKind), + ok = erab_fsm:erab_release(Pid, cmd), {{ok, C}, S}; error -> ?LOG_ERROR("E-RAB ~p is not registered", [erab_uid(ERABId, S)]), @@ -463,11 +464,12 @@ end;
%% 9.1.3.6 E-RAB RELEASE RESPONSE related IEs -handle_ie(?'id-E-RABReleaseListBearerRelComp', C, S) -> +handle_ie([?'id-E-RABReleaseListBearerRelComp'], C, S) -> %% This IE contains a list of E-RABReleaseItemBearerRelComp handle_ies(?'id-E-RABReleaseItemBearerRelComp', C, S);
-handle_ie(?'id-E-RABReleaseItemBearerRelComp', +handle_ie([?'id-E-RABReleaseItemBearerRelComp', + ?'id-E-RABReleaseListBearerRelComp'], #'E-RABReleaseItemBearerRelComp'{'e-RAB-ID' = ERABId} = C, S) -> %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of @@ -480,35 +482,51 @@ end;
%% 9.1.3.7 E-RAB RELEASE INDICATION related IEs -handle_ie(?'id-E-RABReleasedList', C, S) -> +handle_ie([?'id-E-RABReleasedList'], C, S) -> %% This IE contains a list of E-RABItem handle_ies(?'id-E-RABItem', C, S);
+handle_ie([?'id-E-RABItem', + ?'id-E-RABReleasedList'], + #'E-RABItem'{'e-RAB-ID' = ERABId} = C, S) -> + %% poke E-RAB FSM + case erab_fsm_find(ERABId, S) of + {ok, Pid} -> + ok = erab_fsm:erab_release(Pid, ind), + {{ok, C}, S}; + error -> + ?LOG_ERROR("E-RAB ~p is not registered", [erab_uid(ERABId, S)]), + {{error, erab_not_registered}, S} + end; + %% E-RAB MODIFICATION INDICATION related IEs -handle_ie(?'id-E-RABToBeModifiedListBearerModInd', C, S) -> +handle_ie([?'id-E-RABToBeModifiedListBearerModInd'], C, S) -> %% This IE contains a list of BearerModInd, so patch inner IEs handle_ies(?'id-E-RABToBeModifiedItemBearerModInd', C, S);
-handle_ie(?'id-E-RABToBeModifiedItemBearerModInd', +handle_ie([?'id-E-RABToBeModifiedItemBearerModInd', + ?'id-E-RABToBeModifiedListBearerModInd'], #'E-RABToBeModifiedItemBearerModInd'{} = C, S) -> %% TODO: find and poke an E-RAB FSM associated with this E-RAB {{ok, C}, S};
-handle_ie(?'id-E-RABNotToBeModifiedListBearerModInd', C, S) -> +handle_ie([?'id-E-RABNotToBeModifiedListBearerModInd'], C, S) -> %% This IE contains a list of BearerModInd, so patch inner IEs handle_ies(?'id-E-RABNotToBeModifiedItemBearerModInd', C, S);
-handle_ie(?'id-E-RABNotToBeModifiedItemBearerModInd', +handle_ie([?'id-E-RABNotToBeModifiedItemBearerModInd', + ?'id-E-RABNotToBeModifiedListBearerModInd'], #'E-RABNotToBeModifiedItemBearerModInd'{} = C, S) -> %% TODO: find and poke an E-RAB FSM associated with this E-RAB {{ok, C}, S};
%% INITIAL CONTEXT SETUP REQUEST related IEs -handle_ie(?'id-E-RABToBeSetupListCtxtSUReq', C, S) -> +handle_ie([?'id-E-RABToBeSetupListCtxtSUReq'], C, S) -> %% This IE contains a list of CtxtSUReq, so patch inner IEs handle_ies(?'id-E-RABToBeSetupItemCtxtSUReq', C, S);
-handle_ie(?'id-E-RABToBeSetupItemCtxtSUReq', +handle_ie([?'id-E-RABToBeSetupItemCtxtSUReq', + ?'id-E-RABToBeSetupListCtxtSUReq'], #'E-RABToBeSetupItemCtxtSUReq'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S0) -> @@ -524,11 +542,12 @@ end;
%% INITIAL CONTEXT SETUP RESPONSE related IEs -handle_ie(?'id-E-RABSetupListCtxtSURes', C, S) -> +handle_ie([?'id-E-RABSetupListCtxtSURes'], C, S) -> %% This IE contains a list of CtxtSURes, so patch inner IEs handle_ies(?'id-E-RABSetupItemCtxtSURes', C, S);
-handle_ie(?'id-E-RABSetupItemCtxtSURes', +handle_ie([?'id-E-RABSetupItemCtxtSURes', + ?'id-E-RABSetupListCtxtSURes'], #'E-RABSetupItemCtxtSURes'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> @@ -549,8 +568,8 @@ end;
%% Catch-all variant, which should not be called normally -handle_ie(IEI, C, S) -> - ?LOG_ERROR("[BUG] Unhandled S1AP IE: ~p, ~p", [IEI, C]), +handle_ie(P, C, S) -> + ?LOG_ERROR("[BUG] Unhandled S1AP IE: ~p, ~p", [P, C]), {{error, unhandled_ie}, S}.
@@ -559,13 +578,14 @@ %% Additionally look for {MME,eNB}-UE-S1AP-ID IEs and store their values. -type handle_ies_result() :: {ok, list()} | {error, term()}. -spec handle_ies(s1ap_ie_id(), list(), proxy_state()) -> {handle_ies_result(), proxy_state()}. -handle_ies(IEI, IEs, S) -> - handle_ies([], IEI, IEs, S). +handle_ies(IEI, IEs, #proxy_state{path = P} = S) -> + handle_ies([], IEI, IEs, S#proxy_state{path = [IEI | P]}).
-handle_ies(Acc, IEI, [IE | IEs], S0) -> +handle_ies(Acc, IEI, [IE | IEs], + #proxy_state{path = P} = S0) -> case IE of #'ProtocolIE-Field'{id = IEI, value = C0} -> - case handle_ie(IEI, C0, S0) of + case handle_ie(P, C0, S0) of {{ok, C1}, S1} -> NewIE = IE#'ProtocolIE-Field'{value = C1}, handle_ies([NewIE | Acc], IEI, IEs, S1); @@ -582,9 +602,10 @@ handle_ies([IE | Acc], IEI, IEs, S0) end;
-handle_ies(Acc, _IEI, [], S) -> +handle_ies(Acc, IEI, [], + #proxy_state{path = [IEI | P]} = S) -> IEs = lists:reverse(Acc), - {{ok, IEs}, S}. + {{ok, IEs}, S#proxy_state{path = P}}.
build_erab_setup_response_failure(#proxy_state{erabs = ERABs,