fixeria has submitted this change. (
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/37924?usp=email )
Change subject: s1ap_proxy: Support replying errors
......................................................................
s1ap_proxy: Support replying errors
Sometimes it is needed not only to forward, but to transmit messages
initiated at the S1GW. This is the case, for instance, if the MME
wants to initiate an E-RAB establishment, but the co-located UPF is
not available at the time or responds with an error.
This patch adds the generic infrastructure to be able to reply
(errors) and implements the above mentioned specific case for
the S1AP E-RAB SETUP REQUEST procedure.
Change-Id: I242e84fb09b00f4794b6e1aa770f348a0e60aea4
---
M src/s1ap_proxy.erl
M src/sctp_proxy.erl
M test/s1ap_proxy_test.erl
3 files changed, 281 insertions(+), 147 deletions(-)
Approvals:
Jenkins Builder: Verified
pespin: Looks good to me, approved
diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl
index a98514c..0faa42f 100644
--- a/src/s1ap_proxy.erl
+++ b/src/s1ap_proxy.erl
@@ -36,7 +36,7 @@
-export([init/0,
deinit/1,
- handle_pdu/2,
+ process_pdu/2,
handle_exit/2,
encode_pdu/1,
decode_pdu/1]).
@@ -67,6 +67,9 @@
}).
-type proxy_state() :: #proxy_state{}.
+-type proxy_action() :: forward | reply.
+
+-export_type([proxy_action/0]).
%% ------------------------------------------------------------------
%% public API
@@ -85,15 +88,23 @@
%% Process an S1AP PDU
--spec handle_pdu(binary(), proxy_state()) -> {binary(), proxy_state()}.
-handle_pdu(Data, S) when is_binary(Data) ->
- case decode_pdu(Data) of
- {ok, Pdu} ->
- ?LOG_DEBUG("Rx S1AP PDU: ~p", [Pdu]),
- handle_pdu(Data, Pdu, S);
+-spec process_pdu(binary(), proxy_state()) -> {{proxy_action(), binary()},
proxy_state()}.
+process_pdu(OrigData, S0) ->
+ case decode_pdu(OrigData) of
+ {ok, PDU} ->
+ ?LOG_DEBUG("Rx S1AP PDU: ~p", [PDU]),
+ case handle_pdu(PDU, S0) of
+ {{Action, NewPDU}, S1} ->
+ {ok, NewData} = encode_pdu(NewPDU),
+ ?LOG_DEBUG("Tx (~p) S1AP PDU: ~p", [Action, NewPDU]),
+ {{Action, NewData}, S1};
+ {forward, S1} ->
+ ?LOG_DEBUG("Tx (forward) S1AP PDU unmodified"),
+ {{forward, OrigData}, S1}
+ end;
{error, {asn1, Error}} ->
?LOG_ERROR("S1AP PDU decoding failed: ~p", [Error]),
- {Data, S}
+ {{forward, OrigData}, S0} %% XXX: forward as-is or drop?
end.
@@ -128,96 +139,124 @@
'S1AP-PDU-Descriptions':decode('S1AP-PDU', Data).
-%% Helper function for handle_pdu/3.
-%% Attempt to encode a new (modified) S1AP PDU,
-%% return a new binary() on success or Data on error.
--spec handle_pdu_new(binary(), s1ap_pdu(), proxy_state()) -> {binary(),
proxy_state()}.
-handle_pdu_new(Data, NewPdu, S) ->
- case encode_pdu(NewPdu) of
- {ok, NewData} ->
- {NewData, S};
- {error, {asn1, Error}} ->
- ?LOG_ERROR("S1AP PDU encoding failed: ~p", [Error]),
- {Data, S}
- end.
-
-
--spec handle_pdu(binary(), s1ap_pdu(), proxy_state()) -> {binary(), proxy_state()}.
+%% Process an S1AP PDU
+-spec handle_pdu(s1ap_pdu(), proxy_state()) -> {{proxy_action(), s1ap_pdu()},
proxy_state()} |
+ {forward, proxy_state()}.
%% 9.1.3.1 E-RAB SETUP REQUEST
-handle_pdu(Data, {Outcome = initiatingMessage,
- #'InitiatingMessage'{procedureCode = ?'id-E-RABSetup',
- value = Content} = Pdu}, S0) ->
- ?LOG_DEBUG("Patching E-RAB SETUP REQUEST"),
- {IEs, S1} = handle_ies(Content#'E-RABSetupRequest'.protocolIEs,
- ?'id-E-RABToBeSetupListBearerSUReq', S0),
- NewContent = Content#'E-RABSetupRequest'{protocolIEs = IEs},
- handle_pdu_new(Data, {Outcome, Pdu#'InitiatingMessage'{value = NewContent}},
S1);
+handle_pdu({Outcome = initiatingMessage,
+ #'InitiatingMessage'{procedureCode = ?'id-E-RABSetup',
+ value = C0} = Msg}, S0) ->
+ ?LOG_DEBUG("Processing E-RAB SETUP REQUEST"),
+ case handle_ies(C0#'E-RABSetupRequest'.protocolIEs,
+ ?'id-E-RABToBeSetupListBearerSUReq', S0) of
+ {{ok, IEs}, S1} ->
+ C1 = C0#'E-RABSetupRequest'{protocolIEs = IEs},
+ PDU = {Outcome, Msg#'InitiatingMessage'{value = C1}},
+ {{forward, PDU}, S1}; %% forward patched PDU
+ {{error, Reason}, S1} ->
+ ?LOG_NOTICE("Failed to process E-RAB SETUP REQUEST: ~p",
[Reason]),
+ PDU = build_erab_setup_response_failure(S1),
+ {{reply, PDU}, S1} %% reply PDU back to sender
+ end;
%% 9.1.3.2 E-RAB SETUP RESPONSE
-handle_pdu(Data, {Outcome = successfulOutcome,
- #'SuccessfulOutcome'{procedureCode = ?'id-E-RABSetup',
- value = Content} = Pdu}, S0) ->
- ?LOG_DEBUG("Patching E-RAB SETUP RESPONSE"),
- {IEs, S1} = handle_ies(Content#'E-RABSetupResponse'.protocolIEs,
- ?'id-E-RABSetupListBearerSURes', S0),
- NewContent = Content#'E-RABSetupResponse'{protocolIEs = IEs},
- handle_pdu_new(Data, {Outcome, Pdu#'SuccessfulOutcome'{value = NewContent}},
S1);
+handle_pdu({Outcome = successfulOutcome,
+ #'SuccessfulOutcome'{procedureCode = ?'id-E-RABSetup',
+ value = C0} = Msg}, S0) ->
+ ?LOG_DEBUG("Processing E-RAB SETUP RESPONSE"),
+ case handle_ies(C0#'E-RABSetupResponse'.protocolIEs,
+ ?'id-E-RABSetupListBearerSURes', S0) of
+ {{ok, IEs}, S1} ->
+ C1 = C0#'E-RABSetupResponse'{protocolIEs = IEs},
+ PDU = {Outcome, Msg#'SuccessfulOutcome'{value = C1}},
+ {{forward, PDU}, S1}; %% forward patched PDU
+ {{error, Reason}, S1} ->
+ ?LOG_NOTICE("Failed to process E-RAB SETUP RESPONSE: ~p",
[Reason]),
+ {forward, S1} %% XXX: forward as-is or drop?
+ end;
%% TODO: 9.1.3.3 E-RAB MODIFY REQUEST / (Optional) Transport Information
%% 9.1.3.5 E-RAB RELEASE COMMAND
-handle_pdu(Data, {Outcome = initiatingMessage,
- #'InitiatingMessage'{procedureCode =
?'id-E-RABRelease',
- value = Content} = Pdu}, S0) ->
+handle_pdu({Outcome = initiatingMessage,
+ #'InitiatingMessage'{procedureCode = ?'id-E-RABRelease',
+ value = C0} = Msg}, S0) ->
?LOG_DEBUG("Processing E-RAB RELEASE COMMAND"),
- {IEs, S1} = handle_ies(Content#'E-RABReleaseCommand'.protocolIEs,
- ?'id-E-RABToBeReleasedList', S0),
- NewContent = Content#'E-RABReleaseCommand'{protocolIEs = IEs},
- handle_pdu_new(Data, {Outcome, Pdu#'InitiatingMessage'{value = NewContent}},
S1);
+ case handle_ies(C0#'E-RABReleaseCommand'.protocolIEs,
+ ?'id-E-RABToBeReleasedList', S0) of
+ {{ok, IEs}, S1} ->
+ C1 = C0#'E-RABReleaseCommand'{protocolIEs = IEs},
+ PDU = {Outcome, Msg#'InitiatingMessage'{value = C1}},
+ {{forward, PDU}, S1}; %% forward patched PDU
+ {{error, Reason}, S1} ->
+ ?LOG_NOTICE("Failed to process E-RAB RELEASE COMMAND: ~p",
[Reason]),
+ {forward, S1} %% XXX: forward as-is or drop?
+ end;
%% 9.1.3.6 E-RAB RELEASE RESPONSE
-handle_pdu(Data, {Outcome = successfulOutcome,
- #'SuccessfulOutcome'{procedureCode =
?'id-E-RABRelease',
- value = Content} = Pdu}, S0) ->
+handle_pdu({Outcome = successfulOutcome,
+ #'SuccessfulOutcome'{procedureCode = ?'id-E-RABRelease',
+ value = C0} = Msg}, S0) ->
?LOG_DEBUG("Processing E-RAB RELEASE RESPONSE"),
- {IEs, S1} = handle_ies(Content#'E-RABReleaseResponse'.protocolIEs,
- ?'id-E-RABReleaseListBearerRelComp', S0),
- NewContent = Content#'E-RABReleaseResponse'{protocolIEs = IEs},
- handle_pdu_new(Data, {Outcome, Pdu#'SuccessfulOutcome'{value = NewContent}},
S1);
+ case handle_ies(C0#'E-RABReleaseResponse'.protocolIEs,
+ ?'id-E-RABReleaseListBearerRelComp', S0) of
+ {{ok, IEs}, S1} ->
+ C1 = C0#'E-RABReleaseResponse'{protocolIEs = IEs},
+ PDU = {Outcome, Msg#'SuccessfulOutcome'{value = C1}},
+ {{forward, PDU}, S1}; %% forward patched PDU
+ {{error, Reason}, S1} ->
+ ?LOG_NOTICE("Failed to process E-RAB RELEASE RESPONSE: ~p",
[Reason]),
+ {forward, S1} %% XXX: forward as-is or drop?
+ end;
%% 9.1.3.8 E-RAB MODIFICATION INDICATION
-handle_pdu(Data, {Outcome = initiatingMessage,
- #'InitiatingMessage'{procedureCode =
?'id-E-RABModificationIndication',
- value = Content} = Pdu}, S0) ->
- ?LOG_DEBUG("Patching E-RAB MODIFICATION INDICATION"),
- IEs = Content#'E-RABModificationIndication'.protocolIEs,
+handle_pdu({Outcome = initiatingMessage,
+ #'InitiatingMessage'{procedureCode =
?'id-E-RABModificationIndication',
+ value = C0} = Msg}, S0) ->
+ ?LOG_DEBUG("Processing E-RAB MODIFICATION INDICATION"),
+ IEs0 = C0#'E-RABModificationIndication'.protocolIEs,
%% E-RAB to be Modified List
- {IEs1, S1} = handle_ies(IEs, ?'id-E-RABToBeModifiedListBearerModInd', S0),
+ %% TODO: handle {error, Reason}
+ {{ok, IEs1}, S1} = handle_ies(IEs0, ?'id-E-RABToBeModifiedListBearerModInd',
S0),
%% E-RAB not to be Modified List
- {IEs2, S2} = handle_ies(IEs1, ?'id-E-RABNotToBeModifiedListBearerModInd',
S1),
- NewContent = Content#'E-RABModificationIndication'{protocolIEs = IEs2},
- handle_pdu_new(Data, {Outcome, Pdu#'InitiatingMessage'{value = NewContent}},
S2);
+ %% TODO: handle {error, Reason}
+ {{ok, IEs2}, S2} = handle_ies(IEs1,
?'id-E-RABNotToBeModifiedListBearerModInd', S1),
+ C1 = C0#'E-RABModificationIndication'{protocolIEs = IEs2},
+ PDU = {Outcome, Msg#'InitiatingMessage'{value = C1}},
+ {{forward, PDU}, S2};
%% 9.1.4.1 INITIAL CONTEXT SETUP REQUEST
-handle_pdu(Data, {Outcome = initiatingMessage,
- #'InitiatingMessage'{procedureCode =
?'id-InitialContextSetup',
- value = Content} = Pdu}, S0) ->
- ?LOG_DEBUG("Patching INITIAL CONTEXT SETUP REQUEST"),
- {IEs, S1} = handle_ies(Content#'InitialContextSetupRequest'.protocolIEs,
- ?'id-E-RABToBeSetupListCtxtSUReq', S0),
- NewContent = Content#'InitialContextSetupRequest'{protocolIEs = IEs},
- handle_pdu_new(Data, {Outcome, Pdu#'InitiatingMessage'{value = NewContent}},
S1);
+handle_pdu({Outcome = initiatingMessage,
+ #'InitiatingMessage'{procedureCode =
?'id-InitialContextSetup',
+ value = C0} = Msg}, S0) ->
+ ?LOG_DEBUG("Processing INITIAL CONTEXT SETUP REQUEST"),
+ case handle_ies(C0#'InitialContextSetupRequest'.protocolIEs,
+ ?'id-E-RABToBeSetupListCtxtSUReq', S0) of
+ {{ok, IEs}, S1} ->
+ C1 = C0#'InitialContextSetupRequest'{protocolIEs = IEs},
+ PDU = {Outcome, Msg#'InitiatingMessage'{value = C1}},
+ {{forward, PDU}, S1}; %% forward patched PDU
+ {{error, Reason}, S1} ->
+ ?LOG_NOTICE("Failed to process INITIAL CONTEXT SETUP REQUEST: ~p",
[Reason]),
+ {forward, S1} %% XXX: forward as-is or drop?
+ end;
%% 9.1.4.3 INITIAL CONTEXT SETUP RESPONSE
-handle_pdu(Data, {Outcome = successfulOutcome,
- #'SuccessfulOutcome'{procedureCode =
?'id-InitialContextSetup',
- value = Content} = Pdu}, S0) ->
- ?LOG_DEBUG("Patching INITIAL CONTEXT SETUP RESPONSE"),
- {IEs, S1} = handle_ies(Content#'InitialContextSetupResponse'.protocolIEs,
- ?'id-E-RABSetupListCtxtSURes', S0),
- NewContent = Content#'InitialContextSetupResponse'{protocolIEs = IEs},
- handle_pdu_new(Data, {Outcome, Pdu#'SuccessfulOutcome'{value = NewContent}},
S1);
+handle_pdu({Outcome = successfulOutcome,
+ #'SuccessfulOutcome'{procedureCode =
?'id-InitialContextSetup',
+ value = C0} = Msg}, S0) ->
+ ?LOG_DEBUG("Processing INITIAL CONTEXT SETUP RESPONSE"),
+ case handle_ies(C0#'InitialContextSetupResponse'.protocolIEs,
+ ?'id-E-RABSetupListCtxtSURes', S0) of
+ {{ok, IEs}, S1} ->
+ C1 = C0#'InitialContextSetupResponse'{protocolIEs = IEs},
+ PDU = {Outcome, Msg#'SuccessfulOutcome'{value = C1}},
+ {{forward, PDU}, S1}; %% forward patched PDU
+ {{error, Reason}, S1} ->
+ ?LOG_NOTICE("Failed to process INITIAL CONTEXT SETUP RESPONSE: ~p",
[Reason]),
+ {forward, S1} %% XXX: forward as-is or drop?
+ end;
%% TODO: 9.1.5.2 HANDOVER COMMAND :: (O) UL/DL Transport Layer Address
%% TODO: 9.1.5.4 HANDOVER REQUEST :: (M) Transport Layer Address
@@ -227,12 +266,13 @@
%% TODO: 9.1.5.9 PATH SWITCH REQUEST ACKNOWLEDGE :: (M) Transport Layer Address
%% Proxy all other messages unmodified
-handle_pdu(Data, _Pdu, S) ->
- {Data, S}.
+handle_pdu(_PDU, S) ->
+ {forward, S}.
%% Handle a single IE (Information Element)
--spec handle_ie(s1ap_ie(), proxy_state()) -> {s1ap_ie_val(), proxy_state()}.
+-type handle_ie_result() :: {ok, s1ap_ie_val()} | {error, term()}.
+-spec handle_ie(s1ap_ie(), proxy_state()) -> {handle_ie_result(), proxy_state()}.
%% E-RAB SETUP REQUEST related IEs
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABToBeSetupListBearerSUReq',
@@ -247,10 +287,14 @@
'transportLayerAddress' = TLA_In,
'gTP-TEID' = << TEID_In:32/big
>>} = C0,
{Pid, S1} = erab_fsm_start_reg(ERABId, S0),
- {ok, {TEID_Out, TLA_Out}} = erab_fsm:erab_setup_req(Pid, {TEID_In, TLA_In}),
- C1 = C0#'E-RABToBeSetupItemBearerSUReq'{'transportLayerAddress' =
TLA_Out,
- 'gTP-TEID' = << TEID_Out:32/big
>>},
- {C1, S1};
+ case erab_fsm:erab_setup_req(Pid, {TEID_In, TLA_In}) of
+ {ok, {TEID_Out, TLA_Out}} ->
+ C1 =
C0#'E-RABToBeSetupItemBearerSUReq'{'transportLayerAddress' = TLA_Out,
+ 'gTP-TEID' = <<
TEID_Out:32/big >>},
+ {{ok, C1}, S1};
+ {error, Reason} ->
+ {{error, Reason}, S1}
+ end;
%% E-RAB SETUP RESPONSE related IEs
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABSetupListBearerSURes',
@@ -264,16 +308,20 @@
#'E-RABSetupItemBearerSURes'{'e-RAB-ID' = ERABId,
'transportLayerAddress' = TLA_In,
'gTP-TEID' = << TEID_In:32/big >>} =
C0,
- C1 = case erab_fsm_find(ERABId, S) of
+ case erab_fsm_find(ERABId, S) of
{ok, Pid} ->
- {ok, {TEID_Out, TLA_Out}} = erab_fsm:erab_setup_rsp(Pid, {TEID_In, TLA_In}),
- C0#'E-RABSetupItemBearerSURes'{'transportLayerAddress' =
TLA_Out,
- 'gTP-TEID' = << TEID_Out:32/big
>>};
+ case erab_fsm:erab_setup_rsp(Pid, {TEID_In, TLA_In}) of
+ {ok, {TEID_Out, TLA_Out}} ->
+ C1 =
C0#'E-RABSetupItemBearerSURes'{'transportLayerAddress' = TLA_Out,
+ 'gTP-TEID' = <<
TEID_Out:32/big >>},
+ {{ok, C1}, S};
+ {error, Reason} ->
+ {{error, Reason}, S}
+ end;
error ->
?LOG_ERROR("E-RAB-ID ~p is not registered", [ERABId]),
- C0 %% XXX: proxy as-is or drop?
- end,
- {C1, S};
+ {{error, erab_not_registered}, S}
+ end;
%% 9.1.3.5 E-RAB RELEASE COMMAND related IEs
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABToBeReleasedList',
@@ -282,16 +330,17 @@
handle_ies(Content, ?'id-E-RABItem', S);
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABItem',
- value = Content}, S) ->
+ value = C}, S) ->
%% poke E-RAB FSM
- #'E-RABItem'{'e-RAB-ID' = ERABId} = Content,
+ #'E-RABItem'{'e-RAB-ID' = ERABId} = C,
case erab_fsm_find(ERABId, S) of
{ok, Pid} ->
- ok = erab_fsm:erab_release_req(Pid);
+ ok = erab_fsm:erab_release_req(Pid),
+ {{ok, C}, S};
error ->
- ?LOG_ERROR("E-RAB ~p is not registered", [erab_uid(ERABId, S)])
- end,
- {Content, S};
+ ?LOG_ERROR("E-RAB ~p is not registered", [erab_uid(ERABId, S)]),
+ {{error, erab_not_registered}, S}
+ end;
%% 9.1.3.6 E-RAB RELEASE RESPONSE related IEs
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABReleaseListBearerRelComp',
@@ -300,16 +349,17 @@
handle_ies(Content, ?'id-E-RABReleaseItemBearerRelComp', S);
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABReleaseItemBearerRelComp',
- value = Content}, S) ->
+ value = C}, S) ->
%% poke E-RAB FSM
- #'E-RABReleaseItemBearerRelComp'{'e-RAB-ID' = ERABId} = Content,
+ #'E-RABReleaseItemBearerRelComp'{'e-RAB-ID' = ERABId} = C,
case erab_fsm_find(ERABId, S) of
{ok, Pid} ->
- ok = erab_fsm:erab_release_rsp(Pid);
+ ok = erab_fsm:erab_release_rsp(Pid),
+ {{ok, C}, S};
error ->
- ?LOG_ERROR("E-RAB ~p is not registered", [erab_uid(ERABId, S)])
- end,
- {Content, S};
+ ?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(#'ProtocolIE-Field'{id =
?'id-E-RABToBeModifiedListBearerModInd',
@@ -318,9 +368,9 @@
handle_ies(Content, ?'id-E-RABToBeModifiedItemBearerModInd', S);
handle_ie(#'ProtocolIE-Field'{id =
?'id-E-RABToBeModifiedItemBearerModInd',
- value = Content}, S) ->
+ value = C}, S) ->
%% TODO: find and poke an E-RAB FSM associated with this E-RAB
- {Content#'E-RABToBeModifiedItemBearerModInd'{}, S};
+ {{ok, C}, S};
handle_ie(#'ProtocolIE-Field'{id =
?'id-E-RABNotToBeModifiedListBearerModInd',
value = Content}, S) ->
@@ -328,9 +378,9 @@
handle_ies(Content, ?'id-E-RABNotToBeModifiedItemBearerModInd', S);
handle_ie(#'ProtocolIE-Field'{id =
?'id-E-RABNotToBeModifiedItemBearerModInd',
- value = Content}, S) ->
+ value = C}, S) ->
%% TODO: find and poke an E-RAB FSM associated with this E-RAB
- {Content#'E-RABNotToBeModifiedItemBearerModInd'{}, S};
+ {{ok, C}, S};
%% INITIAL CONTEXT SETUP REQUEST related IEs
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABToBeSetupListCtxtSUReq',
@@ -345,10 +395,14 @@
'transportLayerAddress' = TLA_In,
'gTP-TEID' = << TEID_In:32/big >>}
= C0,
{Pid, S1} = erab_fsm_start_reg(ERABId, S0),
- {ok, {TEID_Out, TLA_Out}} = erab_fsm:erab_setup_req(Pid, {TEID_In, TLA_In}),
- C1 = C0#'E-RABToBeSetupItemCtxtSUReq'{'transportLayerAddress' =
TLA_Out,
- 'gTP-TEID' = << TEID_Out:32/big
>>},
- {C1, S1};
+ case erab_fsm:erab_setup_req(Pid, {TEID_In, TLA_In}) of
+ {ok, {TEID_Out, TLA_Out}} ->
+ C1 = C0#'E-RABToBeSetupItemCtxtSUReq'{'transportLayerAddress'
= TLA_Out,
+ 'gTP-TEID' = <<
TEID_Out:32/big >>},
+ {{ok, C1}, S1};
+ {error, Reason} ->
+ {{error, Reason}, S1}
+ end;
%% INITIAL CONTEXT SETUP RESPONSE related IEs
handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABSetupListCtxtSURes',
@@ -362,36 +416,45 @@
#'E-RABSetupItemCtxtSURes'{'e-RAB-ID' = ERABId,
'transportLayerAddress' = TLA_In,
'gTP-TEID' = << TEID_In:32/big >>} =
C0,
- C1 = case erab_fsm_find(ERABId, S) of
+ case erab_fsm_find(ERABId, S) of
{ok, Pid} ->
- {ok, {TEID_Out, TLA_Out}} = erab_fsm:erab_setup_rsp(Pid, {TEID_In, TLA_In}),
- C0#'E-RABSetupItemCtxtSURes'{'transportLayerAddress' =
TLA_Out,
- 'gTP-TEID' = << TEID_Out:32/big
>>};
+ case erab_fsm:erab_setup_rsp(Pid, {TEID_In, TLA_In}) of
+ {ok, {TEID_Out, TLA_Out}} ->
+ C1 =
C0#'E-RABSetupItemCtxtSURes'{'transportLayerAddress' = TLA_Out,
+ 'gTP-TEID' = <<
TEID_Out:32/big >>},
+ {{ok, C1}, S};
+ {error, Reason} ->
+ {{error, Reason}, S}
+ end;
error ->
?LOG_ERROR("E-RAB ~p is not registered", [erab_uid(ERABId, S)]),
- C0 %% XXX: proxy as-is or drop?
- end,
- {C1, S};
+ {{error, erab_not_registered}, S}
+ end;
%% Catch-all variant, which should not be called normally
-handle_ie(#'ProtocolIE-Field'{value = Content} = IE, S) ->
+handle_ie(#'ProtocolIE-Field'{} = IE, S) ->
?LOG_ERROR("[BUG] Unhandled S1AP IE: ~p", [IE]),
- {Content, S}.
+ {{error, unhandled_ie}, S}.
%% Iterate over the given list of 'ProtocolIE-Field' IEs,
%% calling function handle_ie/1 for IEs matching the given IEI.
%% Additionally look for {MME,eNB}-UE-S1AP-ID IEs and store their values.
--spec handle_ies(list(), integer(), proxy_state()) -> {list(), proxy_state()}.
+-type handle_ies_result() :: {ok, list()} | {error, term()}.
+-spec handle_ies(list(), integer(), proxy_state()) -> {handle_ies_result(),
proxy_state()}.
handle_ies(IEs, IEI, S) ->
handle_ies([], IEs, IEI, S).
handle_ies(Acc, [IE | IEs], IEI, S0) ->
case IE of
#'ProtocolIE-Field'{id = IEI} ->
- {Content, S1} = handle_ie(IE, S0),
- NewIE = IE#'ProtocolIE-Field'{value = Content},
- handle_ies([NewIE | Acc], IEs, IEI, S1);
+ case handle_ie(IE, S0) of
+ {{ok, C}, S1} ->
+ NewIE = IE#'ProtocolIE-Field'{value = C},
+ handle_ies([NewIE | Acc], IEs, IEI, S1);
+ {{error, Reason}, S1} ->
+ {{error, Reason}, S1}
+ end;
#'ProtocolIE-Field'{id = ?'id-MME-UE-S1AP-ID', value = Id} ->
S1 = S0#proxy_state{mme_ue_id = Id},
handle_ies([IE | Acc], IEs, IEI, S1);
@@ -403,7 +466,35 @@
end;
handle_ies(Acc, [], _IEI, S) ->
- {lists:reverse(Acc), S}.
+ IEs = lists:reverse(Acc),
+ {{ok, IEs}, S}.
+
+
+build_erab_setup_response_failure(#proxy_state{erabs = ERABs,
+ mme_ue_id = MmeUeId,
+ enb_ue_id = EnbUeId}) ->
+ %% FIXME: Currently we respond with E-RAB-ID of the first E-RAB in the registry.
+ %% Instead, we need to iterate over E-RABs in the REQUEST and reject them all.
+ [{_, _, FirstERABid}|_] = dict:fetch_keys(ERABs),
+ Cause = {transport, 'transport-resource-unavailable'},
+ ERABitem = #'E-RABItem'{'e-RAB-ID' = FirstERABid,
+ cause = Cause},
+ ERABlist = [#'ProtocolIE-Field'{id = ?'id-E-RABItem',
+ criticality = ignore,
+ value = ERABitem}],
+ IEs = [#'ProtocolIE-Field'{id = ?'id-MME-UE-S1AP-ID',
+ criticality = ignore,
+ value = MmeUeId},
+ #'ProtocolIE-Field'{id = ?'id-eNB-UE-S1AP-ID',
+ criticality = ignore,
+ value = EnbUeId},
+ #'ProtocolIE-Field'{id =
?'id-E-RABFailedToSetupListBearerSURes',
+ criticality = ignore,
+ value = ERABlist}],
+ {successfulOutcome,
+ #'SuccessfulOutcome'{procedureCode = ?'id-E-RABSetup',
+ criticality = reject,
+ value = #'E-RABSetupResponse'{protocolIEs = IEs}}}.
-spec erab_fsm_start_reg(erab_id(), proxy_state()) -> {pid(), proxy_state()}.
diff --git a/src/sctp_proxy.erl b/src/sctp_proxy.erl
index 3ebabbd..b37bead 100644
--- a/src/sctp_proxy.erl
+++ b/src/sctp_proxy.erl
@@ -161,11 +161,19 @@
%% Handle an #sctp_sndrcvinfo event (MME -> eNB data)
connected(info, {sctp, _Socket, MmeAddr, MmePort,
{[#sctp_sndrcvinfo{assoc_id = Aid}], Data}},
- #{enb_aid := EnbAid, priv := Priv} = S) ->
+ #{sock := Sock,
+ enb_aid := EnbAid,
+ mme_aid := Aid,
+ priv := Priv} = S) ->
?LOG_DEBUG("MME connection (id=~p, ~p:~p) -> eNB: ~p",
[Aid, MmeAddr, MmePort, Data]),
- {NewData, NewPriv} = handle_pdu(Data, Priv),
- sctp_server:send_data(EnbAid, NewData),
+ {Action, NewPriv} = handle_pdu(Data, Priv),
+ case Action of
+ {forward, FwdData} ->
+ sctp_server:send_data(EnbAid, FwdData);
+ {reply, ReData} ->
+ ok = sctp_client:send_data({Sock, Aid}, ReData)
+ end,
{keep_state, S#{priv := NewPriv}};
%% Handle termination events of the child processes
@@ -212,25 +220,31 @@
%% private API
%% ------------------------------------------------------------------
-%% A safe wrapper for s1ap_proxy:handle_pdu/1
--spec handle_pdu(binary(), term()) -> {binary(), term()}.
-handle_pdu(Data, Priv) when is_binary(Data) ->
- try s1ap_proxy:handle_pdu(Data, Priv) of
- Result -> Result %% {NewData, NewPriv}
+%% A safe wrapper for s1ap_proxy:proc/2
+-spec handle_pdu(binary(), term()) -> {{s1ap_proxy:proxy_action(), binary()},
term()}.
+handle_pdu(OrigData, Priv) when is_binary(OrigData) ->
+ try s1ap_proxy:process_pdu(OrigData, Priv) of
+ Result -> Result %% {Action, NewPriv}
catch
Exception:Reason:StackTrace ->
?LOG_ERROR("An exception occurred: ~p, ~p, ~p", [Exception, Reason,
StackTrace]),
- {Data, Priv} %% proxy as-is
+ {{forward, OrigData}, Priv} %% XXX: proxy as-is or drop?
end.
%% Send a single message to the MME
sctp_send(Data,
#{sock := Sock,
+ enb_aid := EnbAid,
mme_aid := Aid,
priv := Priv} = S) ->
- {NewData, NewPriv} = handle_pdu(Data, Priv),
- ok = sctp_client:send_data({Sock, Aid}, NewData),
+ {Action, NewPriv} = handle_pdu(Data, Priv),
+ case Action of
+ {forward, FwdData} ->
+ ok = sctp_client:send_data({Sock, Aid}, FwdData);
+ {reply, ReData} ->
+ sctp_server:send_data(EnbAid, ReData)
+ end,
S#{priv := NewPriv}.
diff --git a/test/s1ap_proxy_test.erl b/test/s1ap_proxy_test.erl
index 5f6167d..aa3763e 100644
--- a/test/s1ap_proxy_test.erl
+++ b/test/s1ap_proxy_test.erl
@@ -32,6 +32,8 @@
?TC(fun test_s1_setup_req/1)},
{"E-RAB SETUP REQUEST/RESPONSE",
?TC(fun test_e_rab_setup/1)},
+ {"E-RAB SETUP REQUEST (failure)",
+ ?TC(fun test_e_rab_setup_req_fail/1)},
{"E-RAB RELEASE COMMAND/RESPONSE",
?TC(fun test_e_rab_release/1)},
{"E-RAB MODIFICATION INDICATION",
@@ -47,40 +49,57 @@
test_s1_setup_req(S0) ->
SetupReq = s1_setup_req_pdu(),
%% Expect the PDU to be proxied unmodified
- [?_assertEqual({SetupReq, S0}, s1ap_proxy:handle_pdu(SetupReq, S0))].
+ [?_assertEqual({{forward, SetupReq}, S0}, s1ap_proxy:process_pdu(SetupReq, S0))].
test_e_rab_setup(S0) ->
%% [eNB <- MME] E-RAB SETUP REQUEST
SetupReqIn = e_rab_setup_req_pdu(?ADDR_U2C, ?TEID_U2C),
SetupReqExp = e_rab_setup_req_pdu(?ADDR_A2U, ?TEID_A2U),
- {SetupReqOut, S1} = s1ap_proxy:handle_pdu(SetupReqIn, S0),
+ {{forward, SetupReqOut}, S1} = s1ap_proxy:process_pdu(SetupReqIn, S0),
%% [eNB -> MME] E-RAB SETUP RESPONSE
SetupRspIn = e_rab_setup_rsp_pdu(?ADDR_U2A, ?TEID_U2A),
SetupRspExp = e_rab_setup_rsp_pdu(?ADDR_C2U, ?TEID_C2U),
- {SetupRspOut, _S2} = s1ap_proxy:handle_pdu(SetupRspIn, S1),
+ {{forward, SetupRspOut}, _S2} = s1ap_proxy:process_pdu(SetupRspIn, S1),
[?_assertEqual(SetupReqExp, SetupReqOut),
?_assertEqual(SetupRspExp, SetupRspOut)].
+test_e_rab_setup_req_fail(S0) ->
+ %% pfcp_peer:session_establish_req/3 responds with a reject
+ PDU = pfcp_mock:pdu_rsp_reject(session_establishment_response, ?SEID_Loc),
+ pfcp_mock:mock_req(session_establish_req, PDU),
+ %% the linked erab_fsm will terminate abnormally, so trap this
+ process_flag(trap_exit, true),
+ %% eNB <- [S1GW <- MME] E-RAB SETUP REQUEST
+ %% eNB -- [S1GW -> MME] E-RAB SETUP RESPONSE (failure)
+ SetupReqIn = e_rab_setup_req_pdu(?ADDR_U2C, ?TEID_U2C),
+ SetupRspExp = e_rab_setup_rsp_fail_pdu(),
+ {{reply, SetupRspOut}, _S1} = s1ap_proxy:process_pdu(SetupReqIn, S0),
+
+ %% TODO: make sure that the E-RAB FSM has been terminated
+
+ [?_assertEqual(SetupRspExp, SetupRspOut)].
+
+
test_e_rab_release(S0) ->
%% [eNB <- MME] E-RAB SETUP REQUEST
SetupReqIn = e_rab_setup_req_pdu(?ADDR_U2C, ?TEID_U2C),
- {_, S1} = s1ap_proxy:handle_pdu(SetupReqIn, S0),
+ {_, S1} = s1ap_proxy:process_pdu(SetupReqIn, S0),
%% [eNB -> MME] E-RAB SETUP RESPONSE
SetupRspIn = e_rab_setup_rsp_pdu(?ADDR_U2A, ?TEID_U2A),
- {_, S2} = s1ap_proxy:handle_pdu(SetupRspIn, S1),
+ {_, S2} = s1ap_proxy:process_pdu(SetupRspIn, S1),
%% [eNB <- MME] E-RAB RELEASE COMMAND
ReleaseCmd = e_rab_release_cmd_pdu(),
- {ReleaseCmdOut, S3} = s1ap_proxy:handle_pdu(ReleaseCmd, S2),
+ {{forward, ReleaseCmdOut}, S3} = s1ap_proxy:process_pdu(ReleaseCmd, S2),
%% [eNB -> MME] E-RAB RELEASE RESPONSE
ReleaseRsp = e_rab_release_rsp_pdu(),
- {ReleaseRspOut, _S4} = s1ap_proxy:handle_pdu(ReleaseRsp, S3),
+ {{forward, ReleaseRspOut}, _S4} = s1ap_proxy:process_pdu(ReleaseRsp, S3),
%% TODO: make sure that the E-RAB FSM has been terminated
@@ -93,7 +112,7 @@
ModifyIndIn = e_rab_modify_ind_pdu(?ADDR_U2A, ?TEID_U2A),
%% XXX: not implemented, we should actually expect ?ADDR_C2U, ?TEID_C2U
ModifyIndExp = e_rab_modify_ind_pdu(?ADDR_U2A, ?TEID_U2A),
- {ModifyIndOut, _S1} = s1ap_proxy:handle_pdu(ModifyIndIn, S0),
+ {{forward, ModifyIndOut}, _S1} = s1ap_proxy:process_pdu(ModifyIndIn, S0),
[?_assertEqual(ModifyIndExp, ModifyIndOut)].
@@ -101,12 +120,12 @@
%% [eNB <- MME] INITIAL CONTEXT SETUP REQUEST
InitCtxSetupReqIn = initial_context_setup_req_pdu(?ADDR_U2C, ?TEID_U2C),
InitCtxSetupReqExp = initial_context_setup_req_pdu(?ADDR_A2U, ?TEID_A2U),
- {InitCtxSetupReqOut, S1} = s1ap_proxy:handle_pdu(InitCtxSetupReqIn, S0),
+ {{forward, InitCtxSetupReqOut}, S1} = s1ap_proxy:process_pdu(InitCtxSetupReqIn, S0),
%% [eNB -> MME] INITIAL CONTEXT SETUP RESPONSE
InitCtxSetupRspIn = initial_context_setup_rsp_pdu(?ADDR_U2A, ?TEID_U2A),
InitCtxSetupRspExp = initial_context_setup_rsp_pdu(?ADDR_C2U, ?TEID_C2U),
- {InitCtxSetupRspOut, _S2} = s1ap_proxy:handle_pdu(InitCtxSetupRspIn, S1),
+ {{forward, InitCtxSetupRspOut}, _S2} = s1ap_proxy:process_pdu(InitCtxSetupRspIn,
S1),
[?_assertEqual(InitCtxSetupReqExp, InitCtxSetupReqOut),
?_assertEqual(InitCtxSetupRspExp, InitCtxSetupRspOut)].
@@ -165,6 +184,16 @@
>.
+%% [S1GW -> MME] E-RAB SETUP RESPONSE (failure)
+%% Transport-cause: transport-resource-unavailable
+e_rab_setup_rsp_fail_pdu() ->
+ << 16#20, 16#05, 16#00, 16#1a, 16#00, 16#00, 16#03, 16#00,
+ 16#00, 16#40, 16#02, 16#00, 16#07, 16#00, 16#08, 16#40,
+ 16#02, 16#00, 16#09, 16#00, 16#1d, 16#40, 16#07, 16#00,
+ 16#00, 16#23, 16#40, 16#02, 16#0c, 16#20
+ >>.
+
+
%% [eNB <- MME] E-RAB RELEASE COMMAND
%% TODO: make E-RAB IDs configurable
e_rab_release_cmd_pdu() ->
--
To view, visit
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/37924?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: I242e84fb09b00f4794b6e1aa770f348a0e60aea4
Gerrit-Change-Number: 37924
Gerrit-PatchSet: 7
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>