osmith has uploaded this change for review.

View Change

s1ap_proxy: implement patching of E-RAB SETUP REQ/RSP

Change-Id: I88cb261d93b56a4e0ca06ad1d676d1ac16d115bb
---
M src/s1ap_proxy.erl
A test/s1ap_proxy_test.erl
2 files changed, 209 insertions(+), 3 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-s1gw refs/changes/46/37046/1
diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl
index 62701ef..a78f084 100644
--- a/src/s1ap_proxy.erl
+++ b/src/s1ap_proxy.erl
@@ -1,17 +1,25 @@
-module(s1ap_proxy).

--export([handle_pdu/1]).
+-export([handle_pdu/1,
+ encode_pdu/1,
+ decode_pdu/1]).
+
+-include("S1AP-PDU-Descriptions.hrl").
+-include("S1AP-PDU-Contents.hrl").
+-include("S1AP-Containers.hrl").
+-include("S1AP-Constants.hrl").

%% ------------------------------------------------------------------
%% public API
%% ------------------------------------------------------------------

%% Process an S1AP PDU
-handle_pdu(Data) ->
+-spec handle_pdu(binary()) -> binary().
+handle_pdu(Data) when is_binary(Data) ->
case decode_pdu(Data) of
{ok, Pdu} ->
logger:info("S1AP PDU: ~p", [Pdu]),
- Data;
+ handle_pdu(Data, Pdu);
{error, {asn1, Error}} ->
logger:error("S1AP PDU decoding failed: ~p", [Error]),
Data
@@ -22,10 +30,111 @@
%% private API
%% ------------------------------------------------------------------

+%% Encode an S1AP PDU
+-spec encode_pdu(tuple()) -> {ok, binary()} |
+ {error, {asn1, tuple()}}.
+encode_pdu(Pdu) ->
+ 'S1AP-PDU-Descriptions':encode('S1AP-PDU', Pdu).
+
+
%% Decode an S1AP PDU
-spec decode_pdu(binary()) -> {ok, tuple()} |
{error, {asn1, tuple()}}.
decode_pdu(Data) ->
'S1AP-PDU-Descriptions':decode('S1AP-PDU', Data).

+
+%% Helper function for handle_pdu/2.
+%% Attempt to encode a new (modified) S1AP PDU,
+%% return a new binary() on success or Data on error.
+handle_pdu_new(Data, NewPdu) ->
+ case encode_pdu(NewPdu) of
+ {ok, NewData} ->
+ NewData;
+ {error, {asn1, Error}} ->
+ logger:error("S1AP PDU encoding failed: ~p", [Error]),
+ Data
+ end.
+
+
+%% E-RAB SETUP REQUEST
+handle_pdu(Data, {Outcome = initiatingMessage,
+ #'InitiatingMessage'{procedureCode = ?'id-E-RABSetup',
+ value = Content} = Pdu}) ->
+ logger:debug("Patching E-RAB SETUP REQUEST"),
+ IEs = handle_ies(Content#'E-RABSetupRequest'.protocolIEs,
+ ?'id-E-RABToBeSetupListBearerSUReq'),
+ NewContent = Content#'E-RABSetupRequest'{protocolIEs = IEs},
+ handle_pdu_new(Data, {Outcome, Pdu#'InitiatingMessage'{value = NewContent}});
+
+%% E-RAB SETUP RESPONSE
+handle_pdu(Data, {Outcome = successfulOutcome,
+ #'SuccessfulOutcome'{procedureCode = ?'id-E-RABSetup',
+ value = Content} = Pdu}) ->
+ logger:debug("Patching E-RAB SETUP RESPONSE"),
+ IEs = handle_ies(Content#'E-RABSetupResponse'.protocolIEs,
+ ?'id-E-RABSetupListBearerSURes'),
+ NewContent = Content#'E-RABSetupResponse'{protocolIEs = IEs},
+ handle_pdu_new(Data, {Outcome, Pdu#'SuccessfulOutcome'{value = NewContent}});
+
+%% Proxy all other messages unmodified
+handle_pdu(Data, _Pdu) ->
+ Data.
+
+
+%% Handle a single IE (Information Element)
+-spec handle_ie(tuple()) -> tuple().
+
+%% E-RAB SETUP REQUEST related IEs
+handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABToBeSetupListBearerSUReq',
+ value = Content}) ->
+ %% This IE contains a list of BearerSUReq, so patch inner IEs
+ handle_ies(Content, ?'id-E-RABToBeSetupItemBearerSUReq');
+
+handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABToBeSetupItemBearerSUReq',
+ value = Content}) ->
+ Content#'E-RABToBeSetupItemBearerSUReq'{transportLayerAddress = transp_layer_addr()};
+
+%% E-RAB SETUP RESPONSE related IEs
+handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABSetupListBearerSURes',
+ value = Content}) ->
+ %% This IE contains a list of BearerSURes, so patch inner IEs
+ handle_ies(Content, ?'id-E-RABSetupItemBearerSURes');
+
+handle_ie(#'ProtocolIE-Field'{id = ?'id-E-RABSetupItemBearerSURes',
+ value = Content}) ->
+ Content#'E-RABSetupItemBearerSURes'{transportLayerAddress = transp_layer_addr()};
+
+%% Catch-all variant, which should not be called normally
+handle_ie(#'ProtocolIE-Field'{value = Content} = IE) ->
+ logger:error("[BUG] Unhandled S1AP IE: ~p", [IE]),
+ Content.
+
+
+%% Iterate over the given list of 'ProtocolIE-Field' IEs,
+%% calling function handle_ie/1 for IEs matching the given IEI.
+-spec handle_ies(list(), integer()) -> list().
+handle_ies(IEs, IEI) ->
+ handle_ies([], IEs, IEI).
+
+handle_ies(Acc, [IE | IEs], IEI) ->
+ case IE of
+ #'ProtocolIE-Field'{id = IEI} ->
+ NewIE = IE#'ProtocolIE-Field'{value = handle_ie(IE)},
+ handle_ies([NewIE | Acc], IEs, IEI);
+ _ ->
+ handle_ies([IE | Acc], IEs, IEI)
+ end;
+
+handle_ies(Acc, [], _) ->
+ lists:reverse(Acc).
+
+
+%% GTP-U IP address (TransportLayerAddress) to be used while patching
+transp_layer_addr() ->
+ AddrStr = application:get_env(osmo_s1gw, transp_layer_addr, "255.255.255.255"),
+ {ok, Addr} = inet:parse_address(AddrStr),
+ %% sadly, there exists inet:ntoa/1, but not inet:aton/1
+ list_to_binary(tuple_to_list(Addr)).
+
%% vim:set ts=4 sw=4 et:
diff --git a/test/s1ap_proxy_test.erl b/test/s1ap_proxy_test.erl
new file mode 100644
index 0000000..408f468
--- /dev/null
+++ b/test/s1ap_proxy_test.erl
@@ -0,0 +1,88 @@
+-module(s1ap_proxy_test).
+
+-include_lib("eunit/include/eunit.hrl").
+
+%% ------------------------------------------------------------------
+%% testcases
+%% ------------------------------------------------------------------
+
+%% S1 SETUP REQUEST
+s1_setup_req_pdu() ->
+ << 16#00, 16#11, 16#00, 16#1a, 16#00, 16#00, 16#02, 16#00,
+ 16#3b, 16#40, 16#08, 16#00, 16#00, 16#f1, 16#10, 16#00,
+ 16#00, 16#00, 16#00, 16#00, 16#40, 16#00, 16#07, 16#00,
+ 16#0c, 16#0e, 16#40, 16#00, 16#f1, 16#10
+ >>.
+
+s1_setup_req_test() ->
+ OrigData = s1_setup_req_pdu(),
+ %% Expect the PDU to be proxied unmodified
+ ?assertEqual(OrigData, s1ap_proxy:handle_pdu(OrigData)).
+
+
+%% E-RAB SETUP REQUEST
+e_rab_setup_req_pdu(TLA, TEID) when is_binary(TLA),
+ is_binary(TEID) ->
+ << 16#00, 16#05, 16#00, 16#80, 16#9b, 16#00, 16#00, 16#03,
+ 16#00, 16#00, 16#00, 16#02, 16#00, 16#07, 16#00, 16#08,
+ 16#00, 16#02, 16#00, 16#09, 16#00, 16#10, 16#00, 16#80,
+ 16#87, 16#00, 16#00, 16#11, 16#00, 16#80, 16#81, 16#0c,
+ 16#00, 16#05, 16#04, 16#0f, 16#80,
+ TLA/bytes, %% transportLayerAddress (IPv4)
+ TEID/bytes, %% GTP-TEID
+ 16#72, 16#27, 16#c8,
+ 16#1a, 16#bc, 16#ec, 16#11, 16#62, 16#54, 16#c1, 16#01,
+ 16#05, 16#04, 16#03, 16#69, 16#6d, 16#73, 16#05, 16#01,
+ 16#c0, 16#a8, 16#65, 16#02, 16#5e, 16#02, 16#b3, 16#8c,
+ 16#58, 16#32, 16#27, 16#54, 16#80, 16#80, 16#21, 16#10,
+ 16#02, 16#00, 16#00, 16#10, 16#81, 16#06, 16#08, 16#08,
+ 16#08, 16#08, 16#83, 16#06, 16#08, 16#08, 16#04, 16#04,
+ 16#00, 16#0d, 16#04, 16#08, 16#08, 16#08, 16#08, 16#00,
+ 16#0d, 16#04, 16#08, 16#08, 16#04, 16#04, 16#00, 16#03,
+ 16#10, 16#20, 16#01, 16#48, 16#60, 16#48, 16#60, 16#00,
+ 16#00, 16#00, 16#00, 16#00, 16#00, 16#00, 16#00, 16#88,
+ 16#88, 16#00, 16#03, 16#10, 16#20, 16#01, 16#48, 16#60,
+ 16#48, 16#60, 16#00, 16#00, 16#00, 16#00, 16#00, 16#00,
+ 16#00, 16#00, 16#88, 16#44, 16#00, 16#0c, 16#04, 16#ac,
+ 16#16, 16#00, 16#15, 16#00, 16#10, 16#02, 16#05, 16#78
+ >>.
+
+e_rab_setup_req_test() ->
+ %% Original input data
+ TEID = << 16#00, 16#00, 16#00, 16#18 >>,
+ OrigTLA = << 16#ac, 16#16, 16#00, 16#06 >>,
+ OrigData = e_rab_setup_req_pdu(OrigTLA, TEID),
+
+ %% Expected output data
+ ExpTLA = << 16#7f, 16#00, 16#00, 16#01 >>,
+ ExpData = e_rab_setup_req_pdu(ExpTLA, TEID),
+
+ application:set_env(osmo_s1gw, transp_layer_addr, "127.0.0.1"),
+ ?assertEqual(ExpData, s1ap_proxy:handle_pdu(OrigData)).
+
+
+%% E-RAB SETUP RESPONSE
+e_rab_setup_rsp_pdu(TLA, TEID) when is_binary(TLA),
+ is_binary(TEID) ->
+ << 16#20, 16#05, 16#00, 16#22, 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#1c, 16#40, 16#0f, 16#00,
+ 16#00, 16#27, 16#40, 16#0a, 16#0c, 16#1f,
+ TLA/bytes, %% transportLayerAddress (IPv4)
+ TEID/bytes %% GTP-TEID
+ >>.
+
+e_rab_setup_rsp_test() ->
+ %% Original input data
+ TEID = << 16#6c, 16#05, 16#bf, 16#56 >>,
+ OrigTLA = << 16#c0, 16#a8, 16#68, 16#a7 >>,
+ OrigData = e_rab_setup_rsp_pdu(OrigTLA, TEID),
+
+ %% Expected output data
+ ExpTLA = << 16#7f, 16#00, 16#00, 16#01 >>,
+ ExpData = e_rab_setup_rsp_pdu(ExpTLA, TEID),
+
+ application:set_env(osmo_s1gw, transp_layer_addr, "127.0.0.1"),
+ ?assertEqual(ExpData, s1ap_proxy:handle_pdu(OrigData)).
+
+%% vim:set ts=4 sw=4 et:

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

Gerrit-Project: osmo-s1gw
Gerrit-Branch: master
Gerrit-Change-Id: I88cb261d93b56a4e0ca06ad1d676d1ac16d115bb
Gerrit-Change-Number: 37046
Gerrit-PatchSet: 1
Gerrit-Owner: osmith <osmith@sysmocom.de>
Gerrit-CC: fixeria <vyanitskiy@sysmocom.de>
Gerrit-MessageType: newchange