lists.osmocom.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

gerrit-log

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
gerrit-log@lists.osmocom.org

December 2025

  • 1 participants
  • 1175 discussions
[XS] Change in osmo-bsc[master]: vty: rm misleading warnings about DTX on E1 BTS
by fixeria 18 Dec '25

18 Dec '25
Attention is currently required from: falconia. fixeria has posted comments on this change by falconia. ( https://gerrit.osmocom.org/c/osmo-bsc/+/41726?usp=email ) Change subject: vty: rm misleading warnings about DTX on E1 BTS ...................................................................... Patch Set 1: Code-Review+1 -- To view, visit https://gerrit.osmocom.org/c/osmo-bsc/+/41726?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: comment Gerrit-Project: osmo-bsc Gerrit-Branch: master Gerrit-Change-Id: I63626b554d38b1ab094838374968e64193da1c11 Gerrit-Change-Number: 41726 Gerrit-PatchSet: 1 Gerrit-Owner: falconia <falcon(a)freecalypso.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Attention: falconia <falcon(a)freecalypso.org> Gerrit-Comment-Date: Thu, 18 Dec 2025 21:38:46 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1 0
0 0
[M] Change in ...osmo-s1gw[master]: sctp_proxy: use a record for storing state data
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41615?usp=email ) Change subject: sctp_proxy: use a record for storing state data ...................................................................... sctp_proxy: use a record for storing state data Maps are convenient, but they are also more permissive: field names are unchecked at compile time, making it easier for typos or unexpected fields to slip through. Records, on the other hand, are stricter and more declarative. Their structure is explicitly defined, the set of fields is fixed, and the compiler can validate field names. Change-Id: Ice9c255c0cf14db0a216bb078198b9b9c76d22a7 --- M src/sctp_proxy.erl 1 file changed, 61 insertions(+), 45 deletions(-) Approvals: fixeria: Looks good to me, approved Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve osmith: Looks good to me, but someone else must approve diff --git a/src/sctp_proxy.erl b/src/sctp_proxy.erl index 458c53a..5e0d26e 100644 --- a/src/sctp_proxy.erl +++ b/src/sctp_proxy.erl @@ -62,6 +62,18 @@ mme_aid => gen_sctp:assoc_id() }. +-record(state, {enb_aid :: gen_sctp:assoc_id(), + mme_aid :: undefined | gen_sctp:assoc_id(), + enb_conn_info :: sctp_server:conn_info(), + mme_conn_cfg :: sctp_client:cfg(), + tx_queue :: [binary()], + sock :: undefined | gen_sctp:sctp_socket(), + enb_handle :: enb_registry:enb_handle(), + handler :: pid() %% s1ap_proxy process' pid + }). + +-type state() :: #state{}. + -export_type([conn_info/0]). @@ -100,12 +112,12 @@ {ok, EnbHandle} = enb_registry:enb_register(), {ok, Pid} = s1ap_proxy:start_link(EnbHandle, EnbConnInfo), {ok, connecting, - #{enb_aid => maps:get(aid, EnbConnInfo), - enb_conn_info => EnbConnInfo, - mme_conn_cfg => MmeConnCfg, - tx_queue => [], - enb_handle => EnbHandle, - handler => Pid}}. + #state{enb_aid = maps:get(aid, EnbConnInfo), + enb_conn_info = EnbConnInfo, + mme_conn_cfg = MmeConnCfg, + tx_queue = [], + enb_handle = EnbHandle, + handler = Pid}}. callback_mode() -> @@ -114,14 +126,14 @@ %% CONNECTING state connecting(enter, OldState, - #{enb_conn_info := EnbConnInfo, - mme_conn_cfg := MmeConnCfg, - enb_handle := Handle} = S) -> + #state{enb_conn_info = EnbConnInfo, + mme_conn_cfg = MmeConnCfg, + enb_handle = Handle} = S) -> ?LOG_INFO("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]), ok = enb_registry:enb_event(Handle, {?FUNCTION_NAME, EnbConnInfo}), %% Initiate connection establishment with the MME {ok, Sock} = sctp_client:connect(MmeConnCfg), - {next_state, connecting, S#{sock => Sock}, + {next_state, connecting, S#state{sock = Sock}, [{state_timeout, 2_000, conn_est_timeout}]}; %% Handle connection establishment timeout @@ -130,10 +142,10 @@ %% Handle an eNB -> MME data forwarding request (queue) connecting(cast, {send_data, Data}, - #{tx_queue := Pending} = S) -> + #state{tx_queue = Pending} = S) -> s1gw_metrics:ctr_inc(?S1GW_CTR_S1AP_PROXY_UPLINK_PACKETS_QUEUED), s1gw_metrics:gauge_inc(?S1GW_GAUGE_S1AP_PROXY_UPLINK_PACKETS_QUEUED), - {keep_state, S#{tx_queue => [Data | Pending]}}; + {keep_state, S#state{tx_queue = [Data | Pending]}}; %% Handle an #sctp_assoc_change event (connection state) connecting(info, {sctp, _Socket, MmeAddr, MmePort, @@ -143,7 +155,7 @@ comm_up -> ?LOG_NOTICE("MME connection (id=~p, ~p:~p) established", [Aid, MmeAddr, MmePort]), - {next_state, connected, S#{mme_aid => Aid}}; + {next_state, connected, S#state{mme_aid = Aid}}; _ -> ?LOG_NOTICE("MME connection establishment failed: ~p", [ConnState]), {stop, {shutdown, conn_est_fail}} @@ -155,7 +167,7 @@ %% CONNECTED state connected(enter, OldState, - #{enb_handle := Handle} = S0) -> + #state{enb_handle = Handle} = S0) -> ?LOG_INFO("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]), MmeConnInfo = conn_info(?FUNCTION_NAME, S0), ok = enb_registry:enb_event(Handle, {?FUNCTION_NAME, MmeConnInfo}), @@ -188,7 +200,7 @@ stream = SID, ssn = SSN, tsn = TSN}], Data}}, - #{mme_aid := MmeAid} = S) -> + #state{mme_aid = MmeAid} = S) -> ?LOG_DEBUG("MME connection (id=~p, ~p:~p) -> eNB: ~p", [MmeAid, MmeAddr, MmePort, #{tsn => TSN, sid => SID, ssn => SSN, @@ -231,19 +243,22 @@ terminate(Reason, State, - #{handler := Pid, - enb_handle := Handle} = S) -> + #state{handler = Pid, + enb_handle = Handle, + sock = Sock, + mme_aid = MmeAid}) -> ?LOG_NOTICE("Terminating in state ~p, reason ~p", [State, Reason]), enb_registry:enb_unregister(Handle), s1ap_proxy:shutdown(Pid), - case S of - #{sock := Sock, - mme_aid := Aid} -> - sctp_common:shutdown({Sock, Aid}), - gen_sctp:close(Sock); - #{sock := Sock} -> - gen_sctp:close(Sock); - _ -> ok + case Sock of + undefined -> ok; + _ -> + case MmeAid of + undefined -> ok; + _ -> + sctp_common:shutdown({Sock, MmeAid}) + end, + gen_sctp:close(Sock) end. @@ -251,17 +266,13 @@ %% private API %% ------------------------------------------------------------------ -%% XXX: proper type --type state() :: map(). - - %% Send a single message: eNB -> MME -spec sctp_send_from_enb(binary(), state()) -> ok | {error, term()}. sctp_send_from_enb(Data, - #{sock := Sock, - enb_aid := EnbAid, - mme_aid := MmeAid, - handler := Pid}) -> + #state{sock = Sock, + enb_aid = EnbAid, + mme_aid = MmeAid, + handler = Pid}) -> case s1ap_proxy:process_pdu(Pid, Data) of {forward, FwdData} -> sctp_common:send_data({Sock, MmeAid}, FwdData); @@ -275,10 +286,10 @@ %% Send a single message: eNB <- MME -spec sctp_send_from_mme(binary(), state()) -> ok | {error, term()}. sctp_send_from_mme(Data, - #{sock := Sock, - enb_aid := EnbAid, - mme_aid := MmeAid, - handler := Pid}) -> + #state{sock = Sock, + enb_aid = EnbAid, + mme_aid = MmeAid, + handler = Pid}) -> case s1ap_proxy:process_pdu(Pid, Data) of {forward, FwdData} -> sctp_server:send_data(EnbAid, FwdData); @@ -290,23 +301,28 @@ %% Send pending messages to the MME -sctp_send_pending(#{tx_queue := Pending} = S) -> +-spec sctp_send_pending(state()) -> state(). +sctp_send_pending(#state{tx_queue = Pending} = S) -> sctp_send_pending(lists:reverse(Pending), S). +-spec sctp_send_pending([binary()], state()) -> state(). sctp_send_pending([Data | Pending], S) -> sctp_send_from_enb(Data, S), s1gw_metrics:gauge_dec(?S1GW_GAUGE_S1AP_PROXY_UPLINK_PACKETS_QUEUED), sctp_send_pending(Pending, S); sctp_send_pending([], S) -> - S#{tx_queue := []}. + S#state{tx_queue = []}. -conn_info(State, #{mme_conn_cfg := MmeConnCfg} = S0) -> - S1 = maps:with([enb_aid, mme_aid, handler], S0), - S1#{state => State, - mme_addr => maps:get(raddr, MmeConnCfg), - mme_port => maps:get(rport, MmeConnCfg)}. - +-spec conn_info(atom(), state()) -> conn_info(). +conn_info(State, #state{mme_conn_cfg = MmeConnCfg} = S) -> + Info = #{state => State, + handler => S#state.handler, + mme_addr => maps:get(raddr, MmeConnCfg), + mme_port => maps:get(rport, MmeConnCfg), + enb_aid => S#state.enb_aid, + mme_aid => S#state.mme_aid}, + maps:filter(fun(_K, V) -> V =/= undefined end, Info). %% vim:set ts=4 sw=4 et: -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41615?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: Ice9c255c0cf14db0a216bb078198b9b9c76d22a7 Gerrit-Change-Number: 41615 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[XS] Change in ...osmo-s1gw[master]: sctp_proxy: connecting/3: clarify loop state transition
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41617?usp=email ) Change subject: sctp_proxy: connecting/3: clarify loop state transition ...................................................................... sctp_proxy: connecting/3: clarify loop state transition Change-Id: I085955d1fd8e1c312c0630a2ba821d7bfd0814c1 --- M src/sctp_proxy.erl 1 file changed, 2 insertions(+), 1 deletion(-) Approvals: Jenkins Builder: Verified osmith: 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 diff --git a/src/sctp_proxy.erl b/src/sctp_proxy.erl index 2866fcd..0fbcbf5 100644 --- a/src/sctp_proxy.erl +++ b/src/sctp_proxy.erl @@ -133,7 +133,8 @@ ok = enb_registry:enb_event(Handle, {?FUNCTION_NAME, EnbConnInfo}), %% Initiate connection establishment with the MME {ok, Sock} = sctp_client:connect(MmeConnCfg), - {next_state, connecting, S#state{sock = Sock}, + %% loop transition to enable state_timeout + {next_state, ?FUNCTION_NAME, S#state{sock = Sock}, [{state_timeout, 2_000, conn_est_timeout}]}; %% Handle connection establishment timeout -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41617?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: I085955d1fd8e1c312c0630a2ba821d7bfd0814c1 Gerrit-Change-Number: 41617 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[S] Change in ...osmo-s1gw[master]: sctp_proxy: use lists:filter/2 in sctp_send_pending/2
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41616?usp=email ) Change subject: sctp_proxy: use lists:filter/2 in sctp_send_pending/2 ...................................................................... sctp_proxy: use lists:filter/2 in sctp_send_pending/2 Use less code to achieve the same result. Change-Id: Ic666fd77bc6d04341f3c57140dea5d4a5e616e80 --- M src/sctp_proxy.erl 1 file changed, 3 insertions(+), 9 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/sctp_proxy.erl b/src/sctp_proxy.erl index 5e0d26e..2866fcd 100644 --- a/src/sctp_proxy.erl +++ b/src/sctp_proxy.erl @@ -303,15 +303,9 @@ %% Send pending messages to the MME -spec sctp_send_pending(state()) -> state(). sctp_send_pending(#state{tx_queue = Pending} = S) -> - sctp_send_pending(lists:reverse(Pending), S). - --spec sctp_send_pending([binary()], state()) -> state(). -sctp_send_pending([Data | Pending], S) -> - sctp_send_from_enb(Data, S), - s1gw_metrics:gauge_dec(?S1GW_GAUGE_S1AP_PROXY_UPLINK_PACKETS_QUEUED), - sctp_send_pending(Pending, S); - -sctp_send_pending([], S) -> + [] = lists:filter(fun(Data) -> sctp_send_from_enb(Data, S) =/= ok end, + lists:reverse(Pending)), + s1gw_metrics:gauge_set(?S1GW_GAUGE_S1AP_PROXY_UPLINK_PACKETS_QUEUED, 0), S#state{tx_queue = []}. -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41616?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: Ic666fd77bc6d04341f3c57140dea5d4a5e616e80 Gerrit-Change-Number: 41616 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[M] Change in ...osmo-s1gw[master]: s1ap_proxy: signal GTP-U addr and eNB info to sctp_proxy
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41619?usp=email ) Change subject: s1ap_proxy: signal GTP-U addr and eNB info to sctp_proxy ...................................................................... s1ap_proxy: signal GTP-U addr and eNB info to sctp_proxy Move all interaction with the `enb_registry` and `gtpu_kpi` modules from `s1ap_proxy` to its parent process, `sctp_proxy`. This prepares for a follow-up change in which handling of the S1 SETUP procedure will be fully migrated to `sctp_proxy` - a prerequisite for proper MME pooling support. Change-Id: I87e7d22a4bc5cb7816a167ac7dd29ea917594ce8 Related: SYS#7052 --- M src/s1ap_proxy.erl M src/sctp_proxy.erl M test/s1ap_proxy_test.erl 3 files changed, 78 insertions(+), 55 deletions(-) Approvals: fixeria: Looks good to me, approved pespin: Looks good to me, but someone else must approve osmith: Looks good to me, but someone else must approve Jenkins Builder: Verified diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index b5abad5..8d765d4 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -40,7 +40,7 @@ handle_call/3, handle_cast/2, terminate/2]). --export([start_link/2, +-export([start_link/1, process_pdu/2, fetch_erab/2, fetch_erab_list/1, @@ -69,8 +69,7 @@ -type plmn_id() :: {MCC :: nonempty_string(), MNC :: nonempty_string()}. --record(proxy_state, {conn_info :: sctp_server:conn_info(), - enb_handle :: enb_registry:enb_handle(), +-record(proxy_state, {owner :: pid(), erabs :: dict:dict(K :: erab_uid(), V :: pid()), enb_id :: undefined | non_neg_integer(), @@ -100,12 +99,9 @@ %% public API %% ------------------------------------------------------------------ --spec start_link(EnbHandle, ConnInfo) -> Result - when EnbHandle :: enb_registry:enb_handle(), - ConnInfo :: sctp_server:conn_info(), - Result :: gen_server:start_ret(). -start_link(EnbHandle, ConnInfo) -> - gen_server:start_link(?MODULE, [EnbHandle, ConnInfo], []). +-spec start_link(pid()) -> gen_server:start_ret(). +start_link(Owner) -> + gen_server:start_link(?MODULE, [Owner], []). -type process_pdu_result() :: {proxy_action(), binary()}. @@ -141,11 +137,10 @@ %% gen_server API %% ------------------------------------------------------------------ -init([EnbHandle, ConnInfo]) -> +init([Owner]) -> process_flag(trap_exit, true), - {ok, #proxy_state{enb_handle = EnbHandle, - conn_info = ConnInfo, - erabs = dict:new(), + {ok, #proxy_state{erabs = dict:new(), + owner = Owner, path = []}}. @@ -379,9 +374,7 @@ handle_pdu({successfulOutcome, #'SuccessfulOutcome'{procedureCode = ?'id-S1Setup'}}, S) -> ?LOG_DEBUG("Processing S1 SETUP RESPONSE"), - enb_registry:enb_event(S#proxy_state.enb_handle, - {s1setup, enb_info(S)}), - gtpu_kpi_enb_register(S), + signal_enb_info(S), %% there's nothing to patch in this PDU, so we forward it as-is {forward, S}; @@ -756,8 +749,8 @@ #'E-RABSetupItemBearerSURes'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> - %% indicate eNB's address to the gtpu_kpi module - gtpu_kpi_enb_set_addr({s1ap, tla_str(TLA_In)}), + %% signal eNB's GTP-U address to the parent process + signal_enb_addr(TLA_In, S), %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> @@ -1045,8 +1038,8 @@ #'E-RABSetupItemCtxtSURes'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> - %% indicate eNB's address to the gtpu_kpi module - gtpu_kpi_enb_set_addr({s1ap, tla_str(TLA_In)}), + %% signal eNB's GTP-U address to the parent process + signal_enb_addr(TLA_In, S), %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> @@ -1112,8 +1105,8 @@ #'E-RABAdmittedItem'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> - %% indicate eNB's address to the gtpu_kpi module - gtpu_kpi_enb_set_addr({s1ap, tla_str(TLA_In)}), + %% signal eNB's GTP-U address to the parent process + signal_enb_addr(TLA_In, S), %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> @@ -1245,37 +1238,19 @@ dict:find(UID, ERABs). --spec gtpu_kpi_enb_register(proxy_state()) -> ok. -gtpu_kpi_enb_register(#proxy_state{conn_info = ConnInfo} = S) -> - %% register eNB using its Global-eNB-ID - ok = gtpu_kpi:enb_register(genb_id_str(S)), - %% indicate UL/DL addresses - EnbAddr = inet:ntoa(maps:get(addr, ConnInfo)), - gtpu_kpi_enb_set_addr({sctp, EnbAddr}). - - --spec gtpu_kpi_enb_set_addr({Source, EnbAddr}) -> ok - when Source :: s1ap | sctp, - EnbAddr :: string(). -gtpu_kpi_enb_set_addr({Source, EnbAddr}) -> - gtpu_kpi_enb_set_addr(gtpu_kpi_ul_addr, {Source, ul, EnbAddr}), - gtpu_kpi_enb_set_addr(gtpu_kpi_dl_addr, {Source, dl, EnbAddr}), +%% Signal the eNB's GTP-U address to the parent process +-spec signal_enb_addr(binary(), proxy_state()) -> ok. +signal_enb_addr(TLA, #proxy_state{owner = Pid}) -> + %% TODO: optimize to send this only once + Pid ! {?MODULE, {enb_addr, tla_str(TLA)}}, ok. --spec gtpu_kpi_enb_set_addr(EnvParam, {Source, ULDL, EnbAddr}) -> ok - when EnvParam :: atom(), - Source :: s1ap | sctp, - ULDL :: ul | dl, - EnbAddr :: string(). -gtpu_kpi_enb_set_addr(EnvParam, {Source, ULDL, EnbAddr}) -> - case osmo_s1gw:get_env(EnvParam, s1ap) of - Source -> - ?LOG_DEBUG("GTP-U KPI ~p address ~p learned from ~p", - [ULDL, EnbAddr, Source]), - gtpu_kpi:enb_set_addr({ULDL, EnbAddr}); - Mode -> - ?LOG_DEBUG("GTP-U KPI ~p address mode ~p != ~p", [ULDL, Mode, Source]) - end. +%% Signal eNB info to the parent process +-spec signal_enb_info(proxy_state()) -> ok. +signal_enb_info(#proxy_state{owner = Pid} = S) -> + Pid ! {?MODULE, {enb_info, enb_info(S)}}, + ok. + %% vim:set ts=4 sw=4 et: diff --git a/src/sctp_proxy.erl b/src/sctp_proxy.erl index 0fbcbf5..60f0c11 100644 --- a/src/sctp_proxy.erl +++ b/src/sctp_proxy.erl @@ -69,6 +69,7 @@ tx_queue :: [binary()], sock :: undefined | gen_sctp:sctp_socket(), enb_handle :: enb_registry:enb_handle(), + genb_id_str :: undefined | string(), handler :: pid() %% s1ap_proxy process' pid }). @@ -110,7 +111,7 @@ init([EnbConnInfo, MmeConnCfg]) -> {ok, EnbHandle} = enb_registry:enb_register(), - {ok, Pid} = s1ap_proxy:start_link(EnbHandle, EnbConnInfo), + {ok, Pid} = s1ap_proxy:start_link(self()), {ok, connecting, #state{enb_aid = maps:get(aid, EnbConnInfo), enb_conn_info = EnbConnInfo, @@ -209,6 +210,19 @@ sctp_send_from_mme(Data, S), {keep_state, S}; +%% eNB info indication from s1ap_proxy +connected(info, {s1ap_proxy, {enb_info, EnbInfo}}, + #state{enb_handle = EnbHandle} = S0) -> + enb_registry:enb_event(EnbHandle, {s1setup, EnbInfo}), + S1 = S0#state{genb_id_str = maps:get(genb_id_str, EnbInfo)}, + gtpu_kpi_enb_register(S1), + {keep_state, S1}; + +%% eNB's GTP-U address indication from s1ap_proxy +connected(info, {s1ap_proxy, {enb_addr, Addr}}, S) -> + gtpu_kpi_enb_set_addr({s1ap, Addr}), + {keep_state, S}; + connected(Event, EventData, S) -> handle_event(?FUNCTION_NAME, Event, EventData, S). @@ -320,4 +334,40 @@ mme_aid => S#state.mme_aid}, maps:filter(fun(_K, V) -> V =/= undefined end, Info). + +-spec gtpu_kpi_enb_register(state()) -> ok. +gtpu_kpi_enb_register(#state{genb_id_str = GlobalENBId, + enb_conn_info = EnbConnInfo}) -> + %% register eNB using its Global-eNB-ID + ok = gtpu_kpi:enb_register(GlobalENBId), + %% indicate UL/DL addresses + EnbAddr = inet:ntoa(maps:get(addr, EnbConnInfo)), + gtpu_kpi_enb_set_addr({sctp, EnbAddr}). + + +-spec gtpu_kpi_enb_set_addr({Source, EnbAddr}) -> ok + when Source :: s1ap | sctp, + EnbAddr :: string(). +gtpu_kpi_enb_set_addr({Source, EnbAddr}) -> + gtpu_kpi_enb_set_addr(gtpu_kpi_ul_addr, {Source, ul, EnbAddr}), + gtpu_kpi_enb_set_addr(gtpu_kpi_dl_addr, {Source, dl, EnbAddr}), + ok. + + +-spec gtpu_kpi_enb_set_addr(EnvParam, {Source, ULDL, EnbAddr}) -> ok + when EnvParam :: atom(), + Source :: s1ap | sctp, + ULDL :: ul | dl, + EnbAddr :: string(). +gtpu_kpi_enb_set_addr(EnvParam, {Source, ULDL, EnbAddr}) -> + case osmo_s1gw:get_env(EnvParam, s1ap) of + Source -> + ?LOG_DEBUG("GTP-U KPI ~p address ~p learned from ~p", + [ULDL, EnbAddr, Source]), + gtpu_kpi:enb_set_addr({ULDL, EnbAddr}); + Mode -> + ?LOG_DEBUG("GTP-U KPI ~p address mode ~p != ~p", [ULDL, Mode, Source]) + end. + + %% vim:set ts=4 sw=4 et: diff --git a/test/s1ap_proxy_test.erl b/test/s1ap_proxy_test.erl index 8e05b99..e376304 100644 --- a/test/s1ap_proxy_test.erl +++ b/test/s1ap_proxy_test.erl @@ -31,9 +31,7 @@ enb_registry:start_link(), gtpu_kpi:start_link(#{enable => false}), {ok, EnbHandle} = enb_registry:enb_register(), - {ok, Pid} = s1ap_proxy:start_link(EnbHandle, - #{addr => {127,0,0,0}, - port => 1337}), + {ok, Pid} = s1ap_proxy:start_link(self()), ok = enb_registry:enb_unregister(EnbHandle), #{handler => Pid}. -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41619?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: I87e7d22a4bc5cb7816a167ac7dd29ea917594ce8 Gerrit-Change-Number: 41619 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[S] Change in ...osmo-s1gw[master]: s1ap_proxy: drop fetch_enb_info/1
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41620?usp=email ) Change subject: s1ap_proxy: drop fetch_enb_info/1 ...................................................................... s1ap_proxy: drop fetch_enb_info/1 This API is no longer used/needed, since we have the enb_registry. Change-Id: I5a5f0dc1374f14fce14b33b3ea237eb083dd302c Related: ada15bf ("enb_registry: new module") --- M src/s1ap_proxy.erl 1 file changed, 0 insertions(+), 11 deletions(-) Approvals: fixeria: Looks good to me, approved osmith: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index 8d765d4..cbd9b10 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -44,7 +44,6 @@ process_pdu/2, fetch_erab/2, fetch_erab_list/1, - fetch_enb_info/1, shutdown/1]). -include_lib("kernel/include/logger.hrl"). @@ -122,12 +121,6 @@ gen_server:call(Pid, ?FUNCTION_NAME). -%% Fetch information about the eNB --spec fetch_enb_info(pid()) -> enb_info(). -fetch_enb_info(Pid) -> - gen_server:call(Pid, ?FUNCTION_NAME). - - -spec shutdown(pid()) -> ok. shutdown(Pid) -> gen_server:stop(Pid). @@ -157,10 +150,6 @@ #proxy_state{erabs = ERABs} = S) -> {reply, dict:to_list(ERABs), S}; -handle_call(fetch_enb_info, _From, - #proxy_state{} = S) -> - {reply, enb_info(S), S}; - handle_call(Info, From, #proxy_state{} = S) -> ?LOG_ERROR("unknown ~p() from ~p: ~p", [?FUNCTION_NAME, From, Info]), -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41620?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: I5a5f0dc1374f14fce14b33b3ea237eb083dd302c Gerrit-Change-Number: 41620 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[M] Change in ...osmo-s1gw[master]: s1ap_proxy: move {en,de}code_pdu/1 to s1ap_utils
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41618?usp=email ) Change subject: s1ap_proxy: move {en,de}code_pdu/1 to s1ap_utils ...................................................................... s1ap_proxy: move {en,de}code_pdu/1 to s1ap_utils This API will be used by other modules in follow-up patches. Change-Id: I582824a4cac5013ba65a6dd0772f05d49dc14b57 Related: SYS#7052 --- M src/s1ap_proxy.erl A src/s1ap_utils.erl 2 files changed, 75 insertions(+), 21 deletions(-) Approvals: Jenkins Builder: Verified osmith: Looks good to me, but someone else must approve fixeria: Looks good to me, approved pespin: Looks good to me, but someone else must approve diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index a241611..b5abad5 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -58,9 +58,6 @@ -include("S1AP-IEs.hrl"). --type s1ap_pdu() :: {initiatingMessage, #'InitiatingMessage'{}} | - {successfulOutcome, #'SuccessfulOutcome'{}} | - {unsuccessfulOutcome, #'UnsuccessfulOutcome'{}}. -type s1ap_ie_id() :: non_neg_integer(). -type s1ap_ie_val() :: tuple(). @@ -309,30 +306,16 @@ inet:ntoa(TLA1). -%% Encode an S1AP PDU --spec encode_pdu(s1ap_pdu()) -> {ok, binary()} | - {error, {asn1, tuple()}}. -encode_pdu(Pdu) -> - 'S1AP-PDU-Descriptions':encode('S1AP-PDU', Pdu). - - -%% Decode an S1AP PDU --spec decode_pdu(binary()) -> {ok, s1ap_pdu()} | - {error, {asn1, tuple()}}. -decode_pdu(Data) -> - 'S1AP-PDU-Descriptions':decode('S1AP-PDU', Data). - - %% Process an S1AP PDU (binary) -spec handle_pdu_bin(binary(), proxy_state()) -> {process_pdu_result(), proxy_state()}. handle_pdu_bin(OrigData, S0) -> ctr_inc(?S1GW_CTR_S1AP_PROXY_IN_PKT_ALL, S0), - try decode_pdu(OrigData) of + try s1ap_utils:decode_pdu(OrigData) of {ok, PDU} -> ?LOG_DEBUG("Rx S1AP PDU: ~p", [PDU]), try handle_pdu(PDU, S0) of {{Action, NewPDU}, S1} -> - {ok, NewData} = encode_pdu(NewPDU), + {ok, NewData} = s1ap_utils:encode_pdu(NewPDU), ?LOG_DEBUG("Tx (~p) S1AP PDU: ~p", [Action, NewPDU]), case Action of forward -> @@ -376,10 +359,10 @@ %% Process an S1AP PDU (decoded) -spec handle_pdu(PDU, S0) -> {Result, S1} - when PDU :: s1ap_pdu(), + when PDU :: s1ap_utils:s1ap_pdu(), S0 :: proxy_state(), S1 :: proxy_state(), - Result :: {forward | reply, s1ap_pdu()} | forward | drop. + Result :: {forward | reply, s1ap_utils:s1ap_pdu()} | forward | drop. %% 9.1.8.4 S1 SETUP REQUEST handle_pdu({initiatingMessage, diff --git a/src/s1ap_utils.erl b/src/s1ap_utils.erl new file mode 100644 index 0000000..ab7180c --- /dev/null +++ b/src/s1ap_utils.erl @@ -0,0 +1,71 @@ +%% Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> +%% Author: Vadim Yanitskiy <vyanitskiy(a)sysmocom.de> +%% +%% All Rights Reserved +%% +%% SPDX-License-Identifier: AGPL-3.0-or-later +%% +%% This program is free software; you can redistribute it and/or modify +%% it under the terms of the GNU Affero General Public License as +%% published by the Free Software Foundation; either version 3 of the +%% License, or (at your option) any later version. +%% +%% This program is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU Affero General Public License +%% along with this program. If not, see <https://www.gnu.org/licenses/>. +%% +%% Additional Permission under GNU AGPL version 3 section 7: +%% +%% If you modify this Program, or any covered work, by linking or +%% combining it with runtime libraries of Erlang/OTP as released by +%% Ericsson on https://www.erlang.org (or a modified version of these +%% libraries), containing parts covered by the terms of the Erlang Public +%% License (https://www.erlang.org/EPLICENSE) the licensors of this +%% Program grant you additional permission to convey the resulting work +%% without the need to license the runtime libraries of Erlang/OTP under +%% the GNU Affero General Public License. Corresponding Source for a +%% non-source form of such a combination shall include the source code +%% for the parts of the runtime libraries of Erlang/OTP used as well as +%% that of the covered work. + +-module(s1ap_utils). + +-export([encode_pdu/1, + decode_pdu/1]). + +-include_lib("kernel/include/logger.hrl"). + +-include("S1AP-PDU-Descriptions.hrl"). + + +%% S1AP PDU (decoded) +-type s1ap_pdu() :: {initiatingMessage, #'InitiatingMessage'{}} | + {successfulOutcome, #'SuccessfulOutcome'{}} | + {unsuccessfulOutcome, #'UnsuccessfulOutcome'{}}. + +-export_type([s1ap_pdu/0]). + + +%% ------------------------------------------------------------------ +%% public API +%% ------------------------------------------------------------------ + +%% Encode an S1AP PDU +-spec encode_pdu(s1ap_pdu()) -> {ok, binary()} | + {error, {asn1, tuple()}}. +encode_pdu(PDU) -> + 'S1AP-PDU-Descriptions':encode('S1AP-PDU', PDU). + + +%% Decode an S1AP PDU +-spec decode_pdu(binary()) -> {ok, s1ap_pdu()} | + {error, {asn1, tuple()}}. +decode_pdu(Data) -> + 'S1AP-PDU-Descriptions':decode('S1AP-PDU', Data). + + +%% vim:set ts=4 sw=4 et: -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41618?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: I582824a4cac5013ba65a6dd0772f05d49dc14b57 Gerrit-Change-Number: 41618 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[M] Change in ...osmo-s1gw[master]: s1ap_utils: add API for S1AP PDU parsing
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41621?usp=email ) Change subject: s1ap_utils: add API for S1AP PDU parsing ...................................................................... s1ap_utils: add API for S1AP PDU parsing This API will be used in a follow-up patch, in which handling of the S1 SETUP procedure will be fully migrated to `sctp_proxy` - a prerequisite for proper MME pooling support. Change-Id: I0aa3847d0f0ae65f13fa6bbfdbcd6ed188dcb04f Related: SYS#7052 --- M src/enb_registry.erl M src/s1ap_proxy.erl M src/s1ap_utils.erl A test/s1ap_utils_test.erl 4 files changed, 188 insertions(+), 52 deletions(-) Approvals: fixeria: Looks good to me, approved osmith: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve diff --git a/src/enb_registry.erl b/src/enb_registry.erl index 0217836..90c3376 100644 --- a/src/enb_registry.erl +++ b/src/enb_registry.erl @@ -80,8 +80,8 @@ reg_time := integer(), %% registration time (monotonic) uptime := non_neg_integer(), %% seconds since reg_time genb_id_str => string(), %% Global-eNB-ID - enb_id => s1ap_proxy:enb_id(), %% eNB-ID - plmn_id => s1ap_proxy:plmn_id(), %% PLMN-ID + enb_id => s1ap_utils:enb_id(), %% eNB-ID + plmn_id => s1ap_utils:plmn_id(), %% PLMN-ID enb_conn_info => sctp_server:conn_info(), %% eNB -> S1GW connection info mme_conn_info => sctp_proxy:conn_info() %% S1GW -> MME connection info }. diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index cbd9b10..43624d2 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -60,19 +60,16 @@ -type s1ap_ie_id() :: non_neg_integer(). -type s1ap_ie_val() :: tuple(). --type enb_id() :: 0..16#fffffff. -type mme_ue_id() :: 0..16#ffffffff. -type enb_ue_id() :: 0..16#ffffff. -type erab_id() :: 0..16#ff. -type erab_uid() :: {mme_ue_id(), erab_id()}. --type plmn_id() :: {MCC :: nonempty_string(), - MNC :: nonempty_string()}. -record(proxy_state, {owner :: pid(), erabs :: dict:dict(K :: erab_uid(), V :: pid()), - enb_id :: undefined | non_neg_integer(), - plmn_id :: undefined | plmn_id(), + enb_id :: undefined | s1ap_utils:enb_id(), + plmn_id :: undefined | s1ap_utils:plmn_id(), genb_id_str :: undefined | string(), mme_ue_id :: undefined | mme_ue_id(), enb_ue_id :: undefined | enb_ue_id(), @@ -83,15 +80,13 @@ -type proxy_state() :: #proxy_state{}. -type proxy_action() :: forward | reply | drop. --type enb_info() :: #{enb_id => enb_id(), - plmn_id => plmn_id(), +-type enb_info() :: #{enb_id => s1ap_utils:enb_id(), + plmn_id => s1ap_utils:plmn_id(), genb_id_str => string() }. -export_type([proxy_action/0, - enb_info/0, - enb_id/0, - plmn_id/0]). + enb_info/0]). %% ------------------------------------------------------------------ @@ -203,41 +198,6 @@ ok. -%% Parse PLMN-ID as per 3GPP TS 24.008, Figure 10.5.13 -%% | MCC digit 2 | MCC digit 1 | octet 1 -%% | MNC digit 3 | MCC digit 3 | octet 2 -%% | MNC digit 2 | MNC digit 1 | octet 3 --spec parse_plmn_id(<< _:24 >>) -> plmn_id(). -parse_plmn_id(<< MCC2:4, MCC1:4, - MNC3:4, MCC3:4, - MNC2:4, MNC1:4 >>) -> - MCC = parse_mcc_mnc(MCC1, MCC2, MCC3), - MNC = parse_mcc_mnc(MNC1, MNC2, MNC3), - {MCC, MNC}. - - --define(UNHEX(H), H + 48). - -parse_mcc_mnc(D1, D2, 16#f) -> - [?UNHEX(D1), ?UNHEX(D2)]; - -parse_mcc_mnc(D1, D2, D3) -> - [?UNHEX(D1), ?UNHEX(D2), ?UNHEX(D3)]. - - --spec parse_enb_id(tuple()) -> enb_id(). -parse_enb_id({'macroENB-ID', << ID:20 >>}) -> ID; -parse_enb_id({'homeENB-ID', << ID:28 >>}) -> ID; -parse_enb_id({'short-macroENB-ID', << ID:18 >>}) -> ID; -parse_enb_id({'long-macroENB-ID', << ID:21 >>}) -> ID. - - --spec genb_id_str(proxy_state()) -> string(). -genb_id_str(#proxy_state{plmn_id = {MCC, MNC}, - enb_id = ENBId}) -> - MCC ++ "-" ++ MNC ++ "-" ++ integer_to_list(ENBId). - - -spec enb_info(proxy_state()) -> enb_info(). enb_info(S) -> Info = #{enb_id => S#proxy_state.enb_id, @@ -686,13 +646,14 @@ #'Global-ENB-ID'{'pLMNidentity' = PLMNId, 'eNB-ID' = ENBId} = C, S0) -> %% store PLMNId/ENBId - S1 = S0#proxy_state{plmn_id = parse_plmn_id(PLMNId), - enb_id = parse_enb_id(ENBId)}, + S1 = S0#proxy_state{plmn_id = s1ap_utils:parse_plmn_id(PLMNId), + enb_id = s1ap_utils:parse_enb_id(ENBId)}, ?LOG_INFO("Global-ENB-ID: PLMN-ID=~p, eNB-ID=~p", [S1#proxy_state.plmn_id, S1#proxy_state.enb_id]), %% use that as a context for logging - GlobalENBId = genb_id_str(S1), + GlobalENBId = s1ap_utils:genb_id_str(#{plmn_id => S1#proxy_state.plmn_id, + enb_id => S1#proxy_state.enb_id}), osmo_s1gw:set_log_prefix("eNB " ++ GlobalENBId), %% register per-eNB metrics ctr_reg_all(GlobalENBId), diff --git a/src/s1ap_utils.erl b/src/s1ap_utils.erl index ab7180c..6f7e6b6 100644 --- a/src/s1ap_utils.erl +++ b/src/s1ap_utils.erl @@ -35,11 +35,19 @@ -module(s1ap_utils). -export([encode_pdu/1, - decode_pdu/1]). + decode_pdu/1, + parse_pdu/1, + parse_plmn_id/1, + parse_enb_id/1, + genb_id_str/1]). -include_lib("kernel/include/logger.hrl"). -include("S1AP-PDU-Descriptions.hrl"). +-include("S1AP-PDU-Contents.hrl"). +-include("S1AP-Containers.hrl"). +-include("S1AP-Constants.hrl"). +-include("S1AP-IEs.hrl"). %% S1AP PDU (decoded) @@ -47,7 +55,28 @@ {successfulOutcome, #'SuccessfulOutcome'{}} | {unsuccessfulOutcome, #'UnsuccessfulOutcome'{}}. --export_type([s1ap_pdu/0]). +%% 9.2.1.1 Message Type +-type s1ap_msg_type() :: {Proc :: non_neg_integer(), + Type :: initiatingMessage | successfulOutcome | unsuccessfulOutcome}. + +%% S1AP PDU (decoded, unrolled) +-type s1ap_pdu_info() :: {MsgType :: s1ap_msg_type(), + Content :: proplists:proplist()}. + +-export_type([s1ap_pdu/0, + s1ap_msg_type/0, + s1ap_pdu_info/0]). + + +-type enb_id() :: 0..16#fffffff. +-type plmn_id() :: {MCC :: nonempty_string(), + MNC :: nonempty_string()}. +-type genb_id() :: #{enb_id => enb_id(), + plmn_id => plmn_id()}. + +-export_type([enb_id/0, + plmn_id/0, + genb_id/0]). %% ------------------------------------------------------------------ @@ -68,4 +97,114 @@ 'S1AP-PDU-Descriptions':decode('S1AP-PDU', Data). +%% Parse an S1AP PDU +-spec parse_pdu(binary()) -> s1ap_pdu_info() | {error, term()}. +parse_pdu(Data) -> + try decode_pdu(Data) of + {ok, PDU} -> + {MsgType, IEs} = unroll_pdu(PDU), + {MsgType, parse_ies(IEs)}; + {error, Error} -> + ?LOG_ERROR("S1AP PDU decoding failed: ~p", [Error]), + {error, {decode_pdu, Error}} + catch + Exception:Reason:StackTrace -> + ?LOG_ERROR("An exception occurred: ~p, ~p, ~p", [Exception, Reason, StackTrace]), + {error, decode_pdu} + end. + + +-spec genb_id_str(genb_id()) -> string(). +genb_id_str(#{plmn_id := {MCC, MNC}, + enb_id := ENBId}) -> + MCC ++ "-" ++ MNC ++ "-" ++ integer_to_list(ENBId). + + +%% ------------------------------------------------------------------ +%% private API +%% ------------------------------------------------------------------ + +%% Unroll a decoded S1AP PDU (procedure code, type, IEs) +-spec unroll_pdu(s1ap_pdu()) -> s1ap_pdu_info(). +unroll_pdu({Type = initiatingMessage, + #'InitiatingMessage'{procedureCode = PC, + value = {_, IEs}}}) -> {{PC, Type}, IEs}; +unroll_pdu({Type = successfulOutcome, + #'SuccessfulOutcome'{procedureCode = PC, + value = {_, IEs}}}) -> {{PC, Type}, IEs}; +unroll_pdu({Type = unsuccessfulOutcome, + #'UnsuccessfulOutcome'{procedureCode = PC, + value = {_, IEs}}}) -> {{PC, Type}, IEs}. + + +%% Parse PLMN-ID as per 3GPP TS 24.008, Figure 10.5.13 +%% | MCC digit 2 | MCC digit 1 | octet 1 +%% | MNC digit 3 | MCC digit 3 | octet 2 +%% | MNC digit 2 | MNC digit 1 | octet 3 +-spec parse_plmn_id(<< _:24 >>) -> plmn_id(). +parse_plmn_id(<< MCC2:4, MCC1:4, + MNC3:4, MCC3:4, + MNC2:4, MNC1:4 >>) -> + MCC = parse_mcc_mnc(MCC1, MCC2, MCC3), + MNC = parse_mcc_mnc(MNC1, MNC2, MNC3), + {MCC, MNC}. + + +-define(UNHEX(H), H + 48). + +parse_mcc_mnc(D1, D2, 16#f) -> + [?UNHEX(D1), ?UNHEX(D2)]; + +parse_mcc_mnc(D1, D2, D3) -> + [?UNHEX(D1), ?UNHEX(D2), ?UNHEX(D3)]. + + +-spec parse_enb_id(tuple()) -> enb_id(). +parse_enb_id({'macroENB-ID', << ID:20 >>}) -> ID; +parse_enb_id({'homeENB-ID', << ID:28 >>}) -> ID; +parse_enb_id({'short-macroENB-ID', << ID:18 >>}) -> ID; +parse_enb_id({'long-macroENB-ID', << ID:21 >>}) -> ID. + + +-type s1ap_ie_id() :: non_neg_integer(). +-type s1ap_ie_val() :: tuple(). + +-spec parse_ie(tuple()) -> proplists:property(). +-spec parse_ie(s1ap_ie_id(), s1ap_ie_val()) -> term(). + +parse_ie(#'ProtocolIE-Field'{id = IEI, value = C}) -> + {IEI, parse_ie(IEI, C)}; + +parse_ie(#'ProtocolExtensionField'{id = IEI, extensionValue = C}) -> + {IEI, parse_ie(IEI, C)}; + +parse_ie(IE) -> + ?LOG_ERROR("Unknown IE format: ~p", [IE]), + {unknown, IE}. + + +%% 9.2.1.37 Global eNB ID +parse_ie(?'id-Global-ENB-ID', + #'Global-ENB-ID'{'pLMNidentity' = PLMNId, + 'eNB-ID' = EnbId}) -> + #{plmn_id => parse_plmn_id(PLMNId), + enb_id => parse_enb_id(EnbId)}; + +%% 9.1.8.4 Supported TAs +%% 9.2.3.7 Broadcast TAC +parse_ie(?'id-SupportedTAs', TAs) -> + %% TODO: Broadcast PLMNs + [TAC || #'SupportedTAs-Item'{tAC = << TAC:16 >>} <- TAs]; + +%% For all other IEIs return the contents as-is. +parse_ie(_IEI, C) -> C. + + +%% Iterate over the given list of S1AP IEs, calling parse_ie/2 for each. +%% The result is a proplist containing parsed IEs. +-spec parse_ies(list()) -> proplists:proplist(). +parse_ies(IEs) -> + lists:map(fun parse_ie/1, IEs). + + %% vim:set ts=4 sw=4 et: diff --git a/test/s1ap_utils_test.erl b/test/s1ap_utils_test.erl new file mode 100644 index 0000000..1f3279b --- /dev/null +++ b/test/s1ap_utils_test.erl @@ -0,0 +1,36 @@ +-module(s1ap_utils_test). + +-include_lib("eunit/include/eunit.hrl"). +-include("S1AP-Constants.hrl"). +-include("S1AP-IEs.hrl"). + + +-define(_assertEqualIE(IEI, Values), + ?_assertEqual(Values, proplists:get_value(IEI, IEs))). + +-define(_assertMatchIE(IEI, Values), + ?_assertMatch(Values, proplists:get_value(IEI, IEs))). + + +%% ------------------------------------------------------------------ +%% actual testcases +%% ------------------------------------------------------------------ + +s1ap_setup_req_test_() -> + {MsgType, IEs} = s1ap_utils:parse_pdu(s1ap_samples:s1_setup_req_pdu()), + [?_assertEqual(MsgType, {?'id-S1Setup', initiatingMessage}), + ?_assertEqualIE(?'id-Global-ENB-ID', #{enb_id => 0, + plmn_id => {"001", "01"}}), + ?_assertEqualIE(?'id-eNBname', undefined), %% optional, not present + ?_assertMatchIE(?'id-SupportedTAs', [12345])]. %% we only parse the TACs + + +s1ap_setup_rsp_test_() -> + {MsgType, IEs} = s1ap_utils:parse_pdu(s1ap_samples:s1_setup_rsp_pdu()), + [?_assertEqual(MsgType, {?'id-S1Setup', successfulOutcome}), + ?_assertEqualIE(?'id-MMEname', "open5gs-mme0"), + ?_assertMatchIE(?'id-ServedGUMMEIs', [#'ServedGUMMEIsItem'{}]), + ?_assertEqualIE(?'id-RelativeMMECapacity', 16#ff)]. + + +%% vim:set ts=4 sw=4 et: -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41621?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: I0aa3847d0f0ae65f13fa6bbfdbcd6ed188dcb04f Gerrit-Change-Number: 41621 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[S] Change in ...osmo-s1gw[master]: Rename sctp_proxy -> enb_proxy
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41623?usp=email ) Change subject: Rename sctp_proxy -> enb_proxy ...................................................................... Rename sctp_proxy -> enb_proxy This module is no longer an abstract SCTP proxy, ever since we started processing and/or patching S1AP PDUs via the s1ap_proxy. Change-Id: I8beaea9aa98dfa1c2e6f1d1c823b033892bd00cb Related: SYS#7052 --- R src/enb_proxy.erl M src/enb_registry.erl M src/osmo_s1gw_sup.erl M src/rest_server.erl 4 files changed, 5 insertions(+), 5 deletions(-) Approvals: osmith: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve fixeria: Looks good to me, approved diff --git a/src/sctp_proxy.erl b/src/enb_proxy.erl similarity index 99% rename from src/sctp_proxy.erl rename to src/enb_proxy.erl index 305d7d7..7a51221 100644 --- a/src/sctp_proxy.erl +++ b/src/enb_proxy.erl @@ -32,7 +32,7 @@ %% for the parts of the runtime libraries of Erlang/OTP used as well as %% that of the covered work. --module(sctp_proxy). +-module(enb_proxy). -behaviour(gen_statem). -behaviour(sctp_client). diff --git a/src/enb_registry.erl b/src/enb_registry.erl index 80d264b..e40d9f5 100644 --- a/src/enb_registry.erl +++ b/src/enb_registry.erl @@ -64,7 +64,7 @@ | s1setup. %% S1 SETUP procedure completed -type enb_event() :: {connecting, sctp_server:conn_info()} - | {connected, sctp_proxy:conn_info()} + | {connected, enb_proxy:conn_info()} | {s1setup, s1ap_utils:genb_id()}. -type enb_filter() :: {genb_id_str, string()} @@ -82,7 +82,7 @@ genb_id => s1ap_utils:genb_id(), %% Global-eNB-ID genb_id_str => string(), %% Global-eNB-ID string enb_conn_info => sctp_server:conn_info(), %% eNB -> S1GW connection info - mme_conn_info => sctp_proxy:conn_info() %% S1GW -> MME connection info + mme_conn_info => enb_proxy:conn_info() %% S1GW -> MME connection info }. -record(state, {enbs :: #{enb_handle() => enb_info()}, diff --git a/src/osmo_s1gw_sup.erl b/src/osmo_s1gw_sup.erl index bde7b54..980b8f3 100644 --- a/src/osmo_s1gw_sup.erl +++ b/src/osmo_s1gw_sup.erl @@ -139,7 +139,7 @@ #{laddr => maps:get(laddr, Cfg, ?ENV_DEFAULT_S1GW_BIND_ADDR), lport => maps:get(lport, Cfg, ?ENV_DEFAULT_S1GW_BIND_PORT), sockopts => sctp_common:gen_sockopts(SockOpts), - handler => sctp_proxy, + handler => enb_proxy, priv => client_cfg()}. diff --git a/src/rest_server.erl b/src/rest_server.erl index 6e1f892..9535939 100644 --- a/src/rest_server.erl +++ b/src/rest_server.erl @@ -120,7 +120,7 @@ case fetch_enb_info(ID) of [EnbInfo | _] -> Pid = maps:get(pid, EnbInfo), - shutdown(sctp_proxy, Pid); + shutdown(enb_proxy, Pid); [] -> {404, [], undefined}; error -> -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41623?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: I8beaea9aa98dfa1c2e6f1d1c823b033892bd00cb Gerrit-Change-Number: 41623 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[L] Change in ...osmo-s1gw[master]: s1ap_proxy: move S1 SETUP handling to sctp_proxy
by fixeria 18 Dec '25

18 Dec '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41622?usp=email ) Change subject: s1ap_proxy: move S1 SETUP handling to sctp_proxy ...................................................................... s1ap_proxy: move S1 SETUP handling to sctp_proxy This is a prerequisite for the MME pooling support. We need to receive the S1 SETUP REQUEST PDU, extract the Global-eNB-ID and TAC(s), and use this information to select an appropriate MME. The PDU must also be cached so it can be sent (or re-sent) to the selected MME(s). The s1ap_proxy module no longer needs to parse S1 SETUP PDUs, as this logic has been moved to the sctp_proxy and implemented using the newly introduced s1ap_utils API. However, s1ap_proxy still requires the Global-eNB-ID for logging and per-eNB counters, so sctp_proxy now provides it through the new s1ap_proxy:set_genb_id/2 API. Change-Id: I9aa67732b418bcdf3f10b2db89a41dda26ee3d4e Related: SYS#7052 --- M src/enb_registry.erl M src/s1ap_proxy.erl M src/sctp_proxy.erl M test/s1ap_proxy_test.erl 4 files changed, 151 insertions(+), 113 deletions(-) Approvals: Jenkins Builder: Verified osmith: 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 diff --git a/src/enb_registry.erl b/src/enb_registry.erl index 90c3376..80d264b 100644 --- a/src/enb_registry.erl +++ b/src/enb_registry.erl @@ -65,7 +65,7 @@ -type enb_event() :: {connecting, sctp_server:conn_info()} | {connected, sctp_proxy:conn_info()} - | {s1setup, s1ap_proxy:enb_info()}. + | {s1setup, s1ap_utils:genb_id()}. -type enb_filter() :: {genb_id_str, string()} | {enb_sctp_aid, gen_sctp:assoc_id()} @@ -79,9 +79,8 @@ state := enb_state(), %% connection state reg_time := integer(), %% registration time (monotonic) uptime := non_neg_integer(), %% seconds since reg_time - genb_id_str => string(), %% Global-eNB-ID - enb_id => s1ap_utils:enb_id(), %% eNB-ID - plmn_id => s1ap_utils:plmn_id(), %% PLMN-ID + genb_id => s1ap_utils:genb_id(), %% Global-eNB-ID + genb_id_str => string(), %% Global-eNB-ID string enb_conn_info => sctp_server:conn_info(), %% eNB -> S1GW connection info mme_conn_info => sctp_proxy:conn_info() %% S1GW -> MME connection info }. @@ -307,8 +306,10 @@ EnbInfo#{state => connected, mme_conn_info => ConnInfo}; -enb_handle_event(EnbInfo, {s1setup, Info}) -> - maps:merge(EnbInfo#{state => s1setup}, Info); +enb_handle_event(EnbInfo, {s1setup, GENBId}) -> + EnbInfo#{state => s1setup, + genb_id => GENBId, + genb_id_str => s1ap_utils:genb_id_str(GENBId)}; enb_handle_event(EnbInfo, Event) -> ?LOG_ERROR("Unhandled event: ~p", [Event]), diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index 43624d2..0903906 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -41,6 +41,7 @@ handle_cast/2, terminate/2]). -export([start_link/1, + set_genb_id/2, process_pdu/2, fetch_erab/2, fetch_erab_list/1, @@ -68,8 +69,6 @@ -record(proxy_state, {owner :: pid(), erabs :: dict:dict(K :: erab_uid(), V :: pid()), - enb_id :: undefined | s1ap_utils:enb_id(), - plmn_id :: undefined | s1ap_utils:plmn_id(), genb_id_str :: undefined | string(), mme_ue_id :: undefined | mme_ue_id(), enb_ue_id :: undefined | enb_ue_id(), @@ -80,13 +79,7 @@ -type proxy_state() :: #proxy_state{}. -type proxy_action() :: forward | reply | drop. --type enb_info() :: #{enb_id => s1ap_utils:enb_id(), - plmn_id => s1ap_utils:plmn_id(), - genb_id_str => string() - }. - --export_type([proxy_action/0, - enb_info/0]). +-export_type([proxy_action/0]). %% ------------------------------------------------------------------ @@ -98,6 +91,11 @@ gen_server:start_link(?MODULE, [Owner], []). +-spec set_genb_id(pid(), string()) -> ok | {error, term()}. +set_genb_id(Pid, GlobalENBId) -> + gen_server:call(Pid, {?FUNCTION_NAME, GlobalENBId}). + + -type process_pdu_result() :: {proxy_action(), binary()}. -spec process_pdu(pid(), binary()) -> process_pdu_result(). process_pdu(Pid, PDU) -> @@ -145,6 +143,20 @@ #proxy_state{erabs = ERABs} = S) -> {reply, dict:to_list(ERABs), S}; +handle_call({set_genb_id, GlobalENBId}, _From, + #proxy_state{genb_id_str = undefined} = S) -> + ?LOG_DEBUG("Global-eNB-ID is set: ~s", [GlobalENBId]), + %% use that as a context for logging + osmo_s1gw:set_log_prefix("eNB " ++ GlobalENBId), + %% register per-eNB metrics + ctr_reg_all(GlobalENBId), + {reply, ok, S#proxy_state{genb_id_str = GlobalENBId}}; + +handle_call({set_genb_id, _GlobalENBId}, _From, + #proxy_state{genb_id_str = GlobalENBId} = S) -> + ?LOG_ERROR("Global-eNB-ID is already set: ~s", [GlobalENBId]), + {reply, {error, ealready}, S}; + handle_call(Info, From, #proxy_state{} = S) -> ?LOG_ERROR("unknown ~p() from ~p: ~p", [?FUNCTION_NAME, From, Info]), @@ -198,15 +210,6 @@ ok. --spec enb_info(proxy_state()) -> enb_info(). -enb_info(S) -> - Info = #{enb_id => S#proxy_state.enb_id, - plmn_id => S#proxy_state.plmn_id, - genb_id_str => S#proxy_state.genb_id_str}, - %% omit fields with Value =:= undefined - maps:filter(fun(_K, V) -> V =/= undefined end, Info). - - %% register a single per-eNB counter -spec ctr_reg(C, GlobalENBId) -> C when C :: [ctr | _], @@ -308,25 +311,6 @@ S1 :: proxy_state(), Result :: {forward | reply, s1ap_utils:s1ap_pdu()} | forward | drop. -%% 9.1.8.4 S1 SETUP REQUEST -handle_pdu({initiatingMessage, - #'InitiatingMessage'{procedureCode = ?'id-S1Setup', - value = C0}}, S0) -> - ?LOG_DEBUG("Processing S1 SETUP REQUEST"), - %% there's nothing to patch in this PDU, so we forward it as-is - %% TODO: check result of handle_ies(), inc. ?S1GW_CTR_S1AP_PROXY_IN_PKT_PROC_ERROR - {_, S1} = handle_ies(?'id-Global-ENB-ID', - C0#'S1SetupRequest'.protocolIEs, S0), - {forward, S1}; - -%% 9.1.8.5 S1 SETUP RESPONSE -handle_pdu({successfulOutcome, - #'SuccessfulOutcome'{procedureCode = ?'id-S1Setup'}}, S) -> - ?LOG_DEBUG("Processing S1 SETUP RESPONSE"), - signal_enb_info(S), - %% there's nothing to patch in this PDU, so we forward it as-is - {forward, S}; - %% 9.1.3.1 E-RAB SETUP REQUEST handle_pdu({Outcome = initiatingMessage, @@ -642,32 +626,6 @@ Result :: {handle_ie_result(), proxy_state()}. -handle_ie([?'id-Global-ENB-ID'], - #'Global-ENB-ID'{'pLMNidentity' = PLMNId, - 'eNB-ID' = ENBId} = C, S0) -> - %% store PLMNId/ENBId - S1 = S0#proxy_state{plmn_id = s1ap_utils:parse_plmn_id(PLMNId), - enb_id = s1ap_utils:parse_enb_id(ENBId)}, - ?LOG_INFO("Global-ENB-ID: PLMN-ID=~p, eNB-ID=~p", - [S1#proxy_state.plmn_id, - S1#proxy_state.enb_id]), - %% use that as a context for logging - GlobalENBId = s1ap_utils:genb_id_str(#{plmn_id => S1#proxy_state.plmn_id, - enb_id => S1#proxy_state.enb_id}), - osmo_s1gw:set_log_prefix("eNB " ++ GlobalENBId), - %% register per-eNB metrics - ctr_reg_all(GlobalENBId), - %% increment per-eNB ?S1GW_CTR_S1AP_PROXY_IN_PKT_ALL, if needed - %% this is needed to count S1 Setup Req correctly - S2 = case S0#proxy_state.genb_id_str of - GlobalENBId -> S1; - _ -> - Ctr = s1gw_metrics:enb_metric(?S1GW_CTR_S1AP_PROXY_IN_PKT_ALL, GlobalENBId), - s1gw_metrics:ctr_inc(Ctr), - S1#proxy_state{genb_id_str = GlobalENBId} - end, - {{ok, C}, S2}; - %% E-RAB SETUP REQUEST related IEs handle_ie([?'id-E-RABToBeSetupListBearerSUReq'], C, S) -> %% This IE contains a list of BearerSUReq, so patch inner IEs @@ -1196,11 +1154,4 @@ ok. -%% Signal eNB info to the parent process --spec signal_enb_info(proxy_state()) -> ok. -signal_enb_info(#proxy_state{owner = Pid} = S) -> - Pid ! {?MODULE, {enb_info, enb_info(S)}}, - ok. - - %% vim:set ts=4 sw=4 et: diff --git a/src/sctp_proxy.erl b/src/sctp_proxy.erl index 60f0c11..305d7d7 100644 --- a/src/sctp_proxy.erl +++ b/src/sctp_proxy.erl @@ -1,4 +1,4 @@ -%% Copyright (C) 2024 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> +%% Copyright (C) 2024-2025 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> %% Author: Vadim Yanitskiy <vyanitskiy(a)sysmocom.de> %% %% All Rights Reserved @@ -38,6 +38,8 @@ -export([init/1, callback_mode/0, + wait_s1setup_req/3, + wait_s1setup_rsp/3, connecting/3, connected/3, code_change/4, @@ -53,6 +55,8 @@ -include("s1gw_metrics.hrl"). +-include("S1AP-Constants.hrl"). + -type conn_info() :: #{state := atom(), handler := pid(), @@ -66,7 +70,7 @@ mme_aid :: undefined | gen_sctp:assoc_id(), enb_conn_info :: sctp_server:conn_info(), mme_conn_cfg :: sctp_client:cfg(), - tx_queue :: [binary()], + s1setup_req :: undefined | binary(), sock :: undefined | gen_sctp:sctp_socket(), enb_handle :: enb_registry:enb_handle(), genb_id_str :: undefined | string(), @@ -111,12 +115,12 @@ init([EnbConnInfo, MmeConnCfg]) -> {ok, EnbHandle} = enb_registry:enb_register(), + enb_registry:enb_event(EnbHandle, {connecting, EnbConnInfo}), {ok, Pid} = s1ap_proxy:start_link(self()), - {ok, connecting, + {ok, wait_s1setup_req, #state{enb_aid = maps:get(aid, EnbConnInfo), enb_conn_info = EnbConnInfo, mme_conn_cfg = MmeConnCfg, - tx_queue = [], enb_handle = EnbHandle, handler = Pid}}. @@ -125,13 +129,50 @@ [state_functions, state_enter]. +%% WAIT_S1SETUP_REQ state +wait_s1setup_req(enter, _OldState, S) -> + ?LOG_INFO("State enter: ~p", [?FUNCTION_NAME]), + {next_state, ?FUNCTION_NAME, S, + [{state_timeout, 5_000, s1setup_req_timeout}]}; + +%% Handle S1 SETUP REQUEST timeout +wait_s1setup_req(state_timeout, s1setup_req_timeout, _S) -> + ?LOG_ERROR("Timeout waiting for S1 SETUP REQUEST from eNB"), + {stop, {shutdown, s1setup_req_timeout}}; + +%% Handle PDUs coming from the eNB +wait_s1setup_req(cast, {send_data, Data}, S) -> + ?LOG_DEBUG("Rx S1AP PDU from eNB: ~p", [Data]), + case s1ap_utils:parse_pdu(Data) of + {{?'id-S1Setup', initiatingMessage}, IEs} -> + %% fetch the Global-eNB-ID IE, convert it to a string + GENBId = proplists:get_value(?'id-Global-ENB-ID', IEs), + GlobalENBId = s1ap_utils:genb_id_str(GENBId), + %% use it as the logging prefix + osmo_s1gw:set_log_prefix("eNB " ++ GlobalENBId), + ?LOG_INFO("Rx S1 SETUP REQUEST from eNB"), + %% signal the Global-eNB-ID to other modules + s1ap_proxy:set_genb_id(S#state.handler, GlobalENBId), + enb_registry:enb_event(S#state.enb_handle, {s1setup, GENBId}), + gtpu_kpi_enb_register(S#state{genb_id_str = GlobalENBId}), + {next_state, connecting, + S#state{s1setup_req = Data, + genb_id_str = GlobalENBId}}; + {{Proc, Type}, IEs} -> + ?LOG_ERROR("Rx unexpected S1AP PDU from eNB: ~p/~p, ~p", [Proc, Type, IEs]), + {stop, {shutdown, s1setup_error}}; + {error, _Error} -> + {stop, {shutdown, s1setup_error}} + end; + +wait_s1setup_req(Event, EventData, S) -> + handle_event(?FUNCTION_NAME, Event, EventData, S). + + %% CONNECTING state connecting(enter, OldState, - #state{enb_conn_info = EnbConnInfo, - mme_conn_cfg = MmeConnCfg, - enb_handle = Handle} = S) -> + #state{mme_conn_cfg = MmeConnCfg} = S) -> ?LOG_INFO("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]), - ok = enb_registry:enb_event(Handle, {?FUNCTION_NAME, EnbConnInfo}), %% Initiate connection establishment with the MME {ok, Sock} = sctp_client:connect(MmeConnCfg), %% loop transition to enable state_timeout @@ -142,12 +183,10 @@ connecting(state_timeout, conn_est_timeout, _S) -> {stop, {shutdown, conn_est_timeout}}; -%% Handle an eNB -> MME data forwarding request (queue) -connecting(cast, {send_data, Data}, - #state{tx_queue = Pending} = S) -> - s1gw_metrics:ctr_inc(?S1GW_CTR_S1AP_PROXY_UPLINK_PACKETS_QUEUED), - s1gw_metrics:gauge_inc(?S1GW_GAUGE_S1AP_PROXY_UPLINK_PACKETS_QUEUED), - {keep_state, S#state{tx_queue = [Data | Pending]}}; +%% Handle PDUs coming from the eNB +connecting(cast, {send_data, Data}, _S) -> + ?LOG_ERROR("Rx unexpected S1AP PDU from eNB: ~p", [Data]), + keep_state_and_data; %% Handle an #sctp_assoc_change event (connection state) connecting(info, {sctp, _Socket, MmeAddr, MmePort, @@ -157,7 +196,10 @@ comm_up -> ?LOG_NOTICE("MME connection (id=~p, ~p:~p) established", [Aid, MmeAddr, MmePort]), - {next_state, connected, S#state{mme_aid = Aid}}; + %% send the S1 SETUP REQUEST PDU to the MME + sctp_send_from_enb(S#state.s1setup_req, + S#state{mme_aid = Aid}), + {next_state, wait_s1setup_rsp, S#state{mme_aid = Aid}}; _ -> ?LOG_NOTICE("MME connection establishment failed: ~p", [ConnState]), {stop, {shutdown, conn_est_fail}} @@ -167,15 +209,75 @@ handle_event(?FUNCTION_NAME, Event, EventData, S). +%% WAIT_S1SETUP_RSP state +wait_s1setup_rsp(enter, OldState, S) -> + ?LOG_INFO("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]), + {next_state, ?FUNCTION_NAME, S, + [{state_timeout, 5_000, s1setup_rsp_timeout}]}; + +%% Handle S1 SETUP RESPONSE timeout +wait_s1setup_rsp(state_timeout, s1setup_rsp_timeout, _S) -> + ?LOG_ERROR("Timeout waiting for S1 SETUP RESPONSE from MME"), + {stop, {shutdown, s1setup_rsp_timeout}}; + +%% Handle PDUs coming from the eNB +wait_s1setup_rsp(cast, {send_data, Data}, _S) -> + ?LOG_ERROR("Rx unexpected S1AP PDU from eNB: ~p", [Data]), + keep_state_and_data; + +%% Handle PDUs coming from the MME +wait_s1setup_rsp(info, {sctp, _Socket, MmeAddr, MmePort, + {[#sctp_sndrcvinfo{assoc_id = MmeAid, + stream = SID, + ssn = SSN, + tsn = TSN}], Data}}, + #state{mme_aid = MmeAid} = S) -> + ?LOG_DEBUG("MME connection (id=~p, ~p:~p) -> eNB: ~p", + [MmeAid, MmeAddr, MmePort, + #{tsn => TSN, sid => SID, ssn => SSN, + len => byte_size(Data), data => Data}]), + case s1ap_utils:parse_pdu(Data) of + {{?'id-S1Setup', successfulOutcome}, _IEs} -> + ?LOG_INFO("Rx S1 SETUP RESPONSE from MME"), + sctp_send_from_mme(Data, S), + {next_state, connected, S}; + {{?'id-S1Setup', unsuccessfulOutcome}, _IEs} -> + ?LOG_NOTICE("Rx S1 SETUP FAILURE from MME"), + sctp_send_from_mme(Data, S), + {stop, {shutdown, s1setup_error}}; + {{Proc, Type}, IEs} -> + ?LOG_ERROR("Rx unexpected S1AP PDU from MME: ~p/~p, ~p", [Proc, Type, IEs]), + {stop, {shutdown, s1setup_error}}; + {error, _Error} -> + {stop, {shutdown, s1setup_error}} + end; + +%% Handle an #sctp_assoc_change event (MME connection state) +%% We may loose connection while waiting for the S1 SETUP RESPONSE +wait_s1setup_rsp(info, {sctp, _Socket, MmeAddr, MmePort, + {[], #sctp_assoc_change{state = ConnState, + assoc_id = Aid}}}, S) -> + case ConnState of + comm_up -> + ?LOG_NOTICE("MME connection (id=~p, ~p:~p) is already established?!?", + [Aid, MmeAddr, MmePort]), + {keep_state, S}; + _ -> + ?LOG_NOTICE("MME connection state: ~p", [ConnState]), + {stop, {shutdown, conn_fail}} + end; + +wait_s1setup_rsp(Event, EventData, S) -> + handle_event(?FUNCTION_NAME, Event, EventData, S). + + %% CONNECTED state connected(enter, OldState, - #state{enb_handle = Handle} = S0) -> + #state{enb_handle = Handle} = S) -> ?LOG_INFO("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]), - MmeConnInfo = conn_info(?FUNCTION_NAME, S0), + MmeConnInfo = conn_info(?FUNCTION_NAME, S), ok = enb_registry:enb_event(Handle, {?FUNCTION_NAME, MmeConnInfo}), - %% Send pending eNB -> MME messages (if any) - S1 = sctp_send_pending(S0), - {keep_state, S1}; + {keep_state, S}; %% Handle an eNB -> MME data forwarding request (forward) connected(cast, {send_data, Data}, S0) -> @@ -210,14 +312,6 @@ sctp_send_from_mme(Data, S), {keep_state, S}; -%% eNB info indication from s1ap_proxy -connected(info, {s1ap_proxy, {enb_info, EnbInfo}}, - #state{enb_handle = EnbHandle} = S0) -> - enb_registry:enb_event(EnbHandle, {s1setup, EnbInfo}), - S1 = S0#state{genb_id_str = maps:get(genb_id_str, EnbInfo)}, - gtpu_kpi_enb_register(S1), - {keep_state, S1}; - %% eNB's GTP-U address indication from s1ap_proxy connected(info, {s1ap_proxy, {enb_addr, Addr}}, S) -> gtpu_kpi_enb_set_addr({s1ap, Addr}), @@ -315,15 +409,6 @@ end. -%% Send pending messages to the MME --spec sctp_send_pending(state()) -> state(). -sctp_send_pending(#state{tx_queue = Pending} = S) -> - [] = lists:filter(fun(Data) -> sctp_send_from_enb(Data, S) =/= ok end, - lists:reverse(Pending)), - s1gw_metrics:gauge_set(?S1GW_GAUGE_S1AP_PROXY_UPLINK_PACKETS_QUEUED, 0), - S#state{tx_queue = []}. - - -spec conn_info(atom(), state()) -> conn_info(). conn_info(State, #state{mme_conn_cfg = MmeConnCfg} = S) -> Info = #{state => State, diff --git a/test/s1ap_proxy_test.erl b/test/s1ap_proxy_test.erl index e376304..2d748bf 100644 --- a/test/s1ap_proxy_test.erl +++ b/test/s1ap_proxy_test.erl @@ -32,6 +32,7 @@ gtpu_kpi:start_link(#{enable => false}), {ok, EnbHandle} = enb_registry:enb_register(), {ok, Pid} = s1ap_proxy:start_link(self()), + s1ap_proxy:set_genb_id(Pid, ?GlobalENBId), ok = enb_registry:enb_unregister(EnbHandle), #{handler => Pid}. -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41622?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: I9aa67732b418bcdf3f10b2db89a41dda26ee3d4e Gerrit-Change-Number: 41622 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • ...
  • 118
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.