Changes
#746 (Mar 17, 2026, 10:16:07 AM)
mme_registry: add backwards compat for old sctp_client config
When 'mme_pool' is absent from the config, automatically populate the
MME pool with a single 'default' entry derived from the 'sctp_client'
section (including legacy mme_loc_addr/mme_rem_addr params), and emit
a deprecation warning. osmo_s1gw_sup:init/1 normalises 'sctp_client'
in the app env (with all defaults applied) before children start, so
mme_registry can safely read it without duplicating the parsing logic.
Change-Id: Ia97fb61bbb5ace6f43d1a6768fb5fb6883158532
Related: SYS#7052
When 'mme_pool' is absent from the config, automatically populate the
MME pool with a single 'default' entry derived from the 'sctp_client'
section (including legacy mme_loc_addr/mme_rem_addr params), and emit
a deprecation warning. osmo_s1gw_sup:init/1 normalises 'sctp_client'
in the app env (with all defaults applied) before children start, so
mme_registry can safely read it without duplicating the parsing logic.
Change-Id: Ia97fb61bbb5ace6f43d1a6768fb5fb6883158532
Related: SYS#7052
enb_proxy: add initial MME pooling support
Rework the CONNECTING state to dynamically select an MME from the pool
via mme_registry:mme_select/1, passing the eNB's Tracking Area Codes
(from the ?'id-SupportedTAs' IE of the S1 SETUP REQUEST) and a list of
already-tried MMEs, so successive attempts pick a different candidate.
On connection failure (SCTP establishment timeout or error), or when the
selected MME rejects the S1 SETUP REQUEST or fails to respond in time,
the FSM re-enters the CONNECTING state rather than terminating. This
triggers another mme_select/1 call with the failed MME added to the
tried_mmes list. S1 SETUP FAILURE PDUs from the MME are intentionally
not forwarded to the eNB, so the retry is fully transparent.
Once mme_select/1 exhausts all candidates it returns 'error'; at that
point the FSM builds and sends an S1 SETUP FAILURE PDU to the eNB and
terminates.
Other changes:
* add close_sock/1, close_conn/1 helpers; simplify terminate/3
* add ?S1GW_CTR_ENB_PROXY_MME_SELECT_ERROR counter
Change-Id: I83dc4a78c78a7b87e87f5ca9a941a168d6c1dc36
Related: SYS#7052
Rework the CONNECTING state to dynamically select an MME from the pool
via mme_registry:mme_select/1, passing the eNB's Tracking Area Codes
(from the ?'id-SupportedTAs' IE of the S1 SETUP REQUEST) and a list of
already-tried MMEs, so successive attempts pick a different candidate.
On connection failure (SCTP establishment timeout or error), or when the
selected MME rejects the S1 SETUP REQUEST or fails to respond in time,
the FSM re-enters the CONNECTING state rather than terminating. This
triggers another mme_select/1 call with the failed MME added to the
tried_mmes list. S1 SETUP FAILURE PDUs from the MME are intentionally
not forwarded to the eNB, so the retry is fully transparent.
Once mme_select/1 exhausts all candidates it returns 'error'; at that
point the FSM builds and sends an S1 SETUP FAILURE PDU to the eNB and
terminates.
Other changes:
* add close_sock/1, close_conn/1 helpers; simplify terminate/3
* add ?S1GW_CTR_ENB_PROXY_MME_SELECT_ERROR counter
Change-Id: I83dc4a78c78a7b87e87f5ca9a941a168d6c1dc36
Related: SYS#7052
enb_proxy: fix stale SCTP events misprocessed during MME pool selection
When closing a connection to one MME and opening a new one to the next,
a SHUTDOWN_COMP event from the old (now-closed) socket can still be
pending in the process mailbox. The sctp_assoc_change handlers in the
'connecting' and 'wait_s1setup_rsp' states were matching '_Socket',
ignoring the socket identity. A stale SHUTDOWN_COMP would therefore
fall into the '_ -> repeat_state_and_data' branch, triggering a spurious
re-entry of 'connecting', which immediately closed the newly established
connection to the next MME and skipped to yet another pool entry.
Fix this by matching the Socket against the current S#state.sock in both
handlers. Events arriving on a previously closed socket no longer match
these clauses and are silently dropped by the catch-all handle_event/4.
This was found thanks to the MME pooling TCs in ttcn3-s1gw-test.
Change-Id: I4211dd343607f045cf4dd33fa568ed580c79dd9f
Related: SYS#7052
When closing a connection to one MME and opening a new one to the next,
a SHUTDOWN_COMP event from the old (now-closed) socket can still be
pending in the process mailbox. The sctp_assoc_change handlers in the
'connecting' and 'wait_s1setup_rsp' states were matching '_Socket',
ignoring the socket identity. A stale SHUTDOWN_COMP would therefore
fall into the '_ -> repeat_state_and_data' branch, triggering a spurious
re-entry of 'connecting', which immediately closed the newly established
connection to the next MME and skipped to yet another pool entry.
Fix this by matching the Socket against the current S#state.sock in both
handlers. Events arriving on a previously closed socket no longer match
these clauses and are silently dropped by the catch-all handle_event/4.
This was found thanks to the MME pooling TCs in ttcn3-s1gw-test.
Change-Id: I4211dd343607f045cf4dd33fa568ed580c79dd9f
Related: SYS#7052
enb_proxy/mme_registry: add per-MME counters for connection events
Register a set of per-MME counters when an MME is added to the pool,
and increment them from enb_proxy at the relevant state transitions:
- selected (MME chosen for a connection attempt)
- conn_est_timeout (SCTP connection establishment timed out)
- conn_est_failure (SCTP connection establishment failed)
- s1setup_rsp (S1 SETUP RESPONSE received successfully)
- s1setup_failure (S1 SETUP FAILURE received from MME)
- s1setup_rsp_timeout (timed out waiting for S1 SETUP RESPONSE)
A new global aggregate counter ?S1GW_CTR_ENB_PROXY_MME_SELECTED is
also added alongside the existing ?S1GW_CTR_ENB_PROXY_MME_SELECT_ERROR.
Change-Id: Ie0149c1ad0754af6d8f5f95d4e8919993eac3760
Related: SYS#7052
Register a set of per-MME counters when an MME is added to the pool,
and increment them from enb_proxy at the relevant state transitions:
- selected (MME chosen for a connection attempt)
- conn_est_timeout (SCTP connection establishment timed out)
- conn_est_failure (SCTP connection establishment failed)
- s1setup_rsp (S1 SETUP RESPONSE received successfully)
- s1setup_failure (S1 SETUP FAILURE received from MME)
- s1setup_rsp_timeout (timed out waiting for S1 SETUP RESPONSE)
A new global aggregate counter ?S1GW_CTR_ENB_PROXY_MME_SELECTED is
also added alongside the existing ?S1GW_CTR_ENB_PROXY_MME_SELECT_ERROR.
Change-Id: Ie0149c1ad0754af6d8f5f95d4e8919993eac3760
Related: SYS#7052
#738 (Mar 10, 2026, 9:36:06 AM)
erab_fsm: fix comment inaccuracies and a typo
Two comments copy-pasted from session_establish were left saying
"ESTABLISH Req" in session_modify and session_delete handlers where
"MODIFY Req" and "DELETE Req" are correct respectively.
Also fix typo "unieue" -> "unique" in init/1.
Change-Id: If84638142988767363aa080012b44082dee39f90
Two comments copy-pasted from session_establish were left saying
"ESTABLISH Req" in session_modify and session_delete handlers where
"MODIFY Req" and "DELETE Req" are correct respectively.
Also fix typo "unieue" -> "unique" in init/1.
Change-Id: If84638142988767363aa080012b44082dee39f90
pfcp_peer: wrap seq_nr at 24-bit boundary
PFCP sequence numbers are 24-bit (3GPP TS 29.244 section 7.2.2.2), but
the counter was incremented without wrapping, eventually exceeding the
type spec after 16,777,215 requests. Introduce PFCP_SEQ_NR_MAX and use
it in the type spec and in the increment expression.
Change-Id: Ie269894add9a82c36698320379df3aca3f7ffea8
PFCP sequence numbers are 24-bit (3GPP TS 29.244 section 7.2.2.2), but
the counter was incremented without wrapping, eventually exceeding the
type spec after 16,777,215 requests. Introduce PFCP_SEQ_NR_MAX and use
it in the type spec and in the increment expression.
Change-Id: Ie269894add9a82c36698320379df3aca3f7ffea8
pfcp_peer: replace deprecated erlang:timestamp/0 with os:system_time/1
erlang:timestamp/0 is deprecated since OTP 18. os:system_time(second)
is the modern replacement and yields the result directly in seconds,
removing the need to reassemble the {MegaSec, Sec, _} tuple.
Change-Id: I4ad886a2222f0cee8a668b42efe8bfac800a55ac
erlang:timestamp/0 is deprecated since OTP 18. os:system_time(second)
is the modern replacement and yields the result directly in seconds,
removing the need to reassemble the {MegaSec, Sec, _} tuple.
Change-Id: I4ad886a2222f0cee8a668b42efe8bfac800a55ac
pfcp_peer: replace watchdog process with a timer
The watchdog process is spawned bare (no link/monitor), so if it
crashes its failure goes unnoticed, and if pfcp_peer restarts the
orphaned watchdog may fire a stale cast at the new process.
The watchdog process exists solely to send a delayed message to
pfcp_peer on timeout, and to be cancelled (by receiving
heartbeat_response) when the response arrives. That's exactly what
erlang:start_timer/3 does natively - no separate process needed.
Change-Id: I8d71ad8300feefb0aecbf690a825a2b4e9f1102c
The watchdog process is spawned bare (no link/monitor), so if it
crashes its failure goes unnoticed, and if pfcp_peer restarts the
orphaned watchdog may fire a stale cast at the new process.
The watchdog process exists solely to send a delayed message to
pfcp_peer on timeout, and to be cancelled (by receiving
heartbeat_response) when the response arrives. That's exactly what
erlang:start_timer/3 does natively - no separate process needed.
Change-Id: I8d71ad8300feefb0aecbf690a825a2b4e9f1102c
s1ap_proxy: fix discarded result of handle_ies/3 in HO REQ ACK handler
The call processing the optional E-RABFailedToSetupListHOReqAck IE did
not pattern-match its return value, causing any errors returned by
handle_ies/3 to be silently swallowed. Bind the result to {_, S2}
to make the intent explicit.
Change-Id: I1c8feeb63fe23876ae443784980e9dc22a450c54
The call processing the optional E-RABFailedToSetupListHOReqAck IE did
not pattern-match its return value, causing any errors returned by
handle_ies/3 to be silently swallowed. Bind the result to {_, S2}
to make the intent explicit.
Change-Id: I1c8feeb63fe23876ae443784980e9dc22a450c54
enb_registry: call enb_metrics_register/1 from a proper place
enb_metrics_register/1 requires the Global-eNB-ID to register
per-eNB metrics. Calling it for every event, which may or may not
contain the Global-eNB-ID, is suboptimal.
Instead, invoke it from the enb_handle_event/2 clause that handles
the {s1setup, GENBId} event, where the Global-Enb-ID is guaranteed
to be available.
Change-Id: I38237463aa9c968f89bf4f195407a18cba7e73c9
enb_metrics_register/1 requires the Global-eNB-ID to register
per-eNB metrics. Calling it for every event, which may or may not
contain the Global-eNB-ID, is suboptimal.
Instead, invoke it from the enb_handle_event/2 clause that handles
the {s1setup, GENBId} event, where the Global-Enb-ID is guaranteed
to be available.
Change-Id: I38237463aa9c968f89bf4f195407a18cba7e73c9
enb_registry: fix pattern match in handle_info 'DOWN' handler
PIDs maps pid() => enb_handle() (an integer), so maps:find/2 returns
{ok, SomeHandle}. The old pattern {ok, Pid} tried to match an integer
against the already-bound pid() variable, which never succeeds.
This handler is only called on abnormal termination (process crash),
so the broken match caused the registry entry to never be cleaned up
in that case. Fix by using a fresh {ok, Handle} binding and removing
the now-redundant follow-up maps:get/2 call.
Change-Id: I4cb044e8071c4ae2fc48c507f8733af6c617ec46
PIDs maps pid() => enb_handle() (an integer), so maps:find/2 returns
{ok, SomeHandle}. The old pattern {ok, Pid} tried to match an integer
against the already-bound pid() variable, which never succeeds.
This handler is only called on abnormal termination (process crash),
so the broken match caused the registry entry to never be cleaned up
in that case. Fix by using a fresh {ok, Handle} binding and removing
the now-redundant follow-up maps:get/2 call.
Change-Id: I4cb044e8071c4ae2fc48c507f8733af6c617ec46
enb_registry: fix duplicate registration check in enb_register/0
next_handle was bound in the function head and reused in the case
pattern {ok, Handle}, turning what looks like a binding into an
equality test against next_handle. The duplicate check therefore
only fired if the previously assigned handle happened to equal the
current next_handle counter, which is essentially never true.
Fix by removing next_handle from the function head pattern and reading
it with S#state.next_handle inside the error branch, so that Handle in
{ok, Handle} is always a fresh binding that matches any existing handle.
Change-Id: Ia64f3e2e79bea055e5c37df9bce91a4bcbf7184c
next_handle was bound in the function head and reused in the case
pattern {ok, Handle}, turning what looks like a binding into an
equality test against next_handle. The duplicate check therefore
only fired if the previously assigned handle happened to equal the
current next_handle counter, which is essentially never true.
Fix by removing next_handle from the function head pattern and reading
it with S#state.next_handle inside the error branch, so that Handle in
{ok, Handle} is always a fresh binding that matches any existing handle.
Change-Id: Ia64f3e2e79bea055e5c37df9bce91a4bcbf7184c
enb_registry: fix addr/port filter logic (use andalso)
In Erlang a function body returns only its last expression. The comma
between the two enb_filter_by_sub_field/2 calls caused the result of
the addr check to be silently discarded, so the filter only tested the
port. Replace the comma with andalso to require both to match.
Change-Id: I8061636cd1077a4f3a9e9d37a31224f5e373becb
In Erlang a function body returns only its last expression. The comma
between the two enb_filter_by_sub_field/2 calls caused the result of
the addr check to be silently discarded, so the filter only tested the
port. Replace the comma with andalso to require both to match.
Change-Id: I8061636cd1077a4f3a9e9d37a31224f5e373becb
enb_registry: rework handling of eNB property updates
The idea of an enb_event/0 containing the current MME connection
state and the associated information (Global-eNB-ID, eNB/MME conn
info) looked promising initially, however turned out to be
impractical in light of ongoing MME pooling related changes.
* remove type enb_state/0
* rename type enb_event/0 -> enb_prop/0
* rename function enb_event/2 -> enb_update/2 (now private)
* enb_prop/0: separate the state from other properties
* enb_update/2: accept a list of enb_prop/0 - enb_proplist/0
* add typed notify_*() helpers that group related property updates,
ensuring consistency and serving as the sole public call sites
* notify_mme_connecting(): explicitly clears mme_conn_info
* openapi: EnbItem.state reflects the actual enb_proxy FSM state
Change-Id: Ib5c5dedce729151cd7350390987fdd008b254ef4
Related: SYS#7052
The idea of an enb_event/0 containing the current MME connection
state and the associated information (Global-eNB-ID, eNB/MME conn
info) looked promising initially, however turned out to be
impractical in light of ongoing MME pooling related changes.
* remove type enb_state/0
* rename type enb_event/0 -> enb_prop/0
* rename function enb_event/2 -> enb_update/2 (now private)
* enb_prop/0: separate the state from other properties
* enb_update/2: accept a list of enb_prop/0 - enb_proplist/0
* add typed notify_*() helpers that group related property updates,
ensuring consistency and serving as the sole public call sites
* notify_mme_connecting(): explicitly clears mme_conn_info
* openapi: EnbItem.state reflects the actual enb_proxy FSM state
Change-Id: Ib5c5dedce729151cd7350390987fdd008b254ef4
Related: SYS#7052
mme_registry: the MME registry (pool) implementation
Change-Id: Id5480222439bf93eca2e994b291c619dff823b01
Related: SYS#7052
Change-Id: Id5480222439bf93eca2e994b291c619dff823b01
Related: SYS#7052
[REST] Add MmeList, MmeAdd, MmeInfo, MmeDelete
Change-Id: Iad249aed99face9e35fd19e0596cf2364ade4c77
Related: SYS#7052
Change-Id: Iad249aed99face9e35fd19e0596cf2364ade4c77
Related: SYS#7052
s1ap_utils_test: fix: expected value goes first
Change-Id: Ib7c6478b95a78c9797e86180a89c098e75b615e0
Change-Id: Ib7c6478b95a78c9797e86180a89c098e75b615e0
s1ap_utils: add API for building S1 SETUP FAILURE PDU
This API will be used in a follow-up patch adding the MME pooling.
Take a chance to add a parsing test for the new PDU blob.
Change-Id: I5a4e060e0a2ebdfbcfafac42f9de2e49ac3583b8
Related: SYS#7052
This API will be used in a follow-up patch adding the MME pooling.
Take a chance to add a parsing test for the new PDU blob.
Change-Id: I5a4e060e0a2ebdfbcfafac42f9de2e49ac3583b8
Related: SYS#7052