Skip to content

Loading builds...

Changes

#767 (Mar 27, 2026, 12:46:08 AM)

pfcp_peer: implement periodic PFCP heartbeat

Add a heartbeat_interval parameter to the pfcp_peer config section.
When non-zero, pfcp_peer sends a periodic Heartbeat Request to the UPF
at the configured interval using a named gen_statem timeout (hb_timer).
The timer is started on entry to the connected state and cancelled on
re-entry to the connecting state.  Default is 10000 ms (10 seconds).

Change-Id: I306324f8eca325202a3fa23125854db9d5eaab38
laforge at

#766 (Mar 26, 2026, 11:51:10 AM)

s1ap_proxy: fix build_erab_setup_response_failure() to report all E-RABs

The previous implementation only included the first E-RAB from the
registry in the failure response (a FIXME was left in place).

Fix it to extract all E-RAB IDs from the original E-RAB SETUP REQUEST
and include each of them in the failure list, as required by 3GPP.

Change-Id: I7933fceb0edcdfdc95ace35416297b11c83f0bc9
Related: osmo-ttcn3-hacks.git I8a5dc338d28013dc85e1ce4b3bdac92cb3b35304
Vadim Yanitskiy at

#765 (Mar 26, 2026, 10:11:09 AM)

pfcp_peer: tune Heartbeat Request/Response logging

Reduce verbosity of the Tx/Rx Heartbeat Req/Resp log messages from
LOG_INFO to LOG_DEBUG in preparation for periodic heartbeat support.
Without this change, periodic heartbeats would flood the logging.

Change-Id: I6435cdf64fd86fa5358d57ea4d56dcad8eb9e69e
Vadim Yanitskiy at

#763 (Mar 25, 2026, 2:16:07 PM)

[REST] PfcpAssocState: field 'lrts' is required

The local RTS is always known and thus always gets included regardless
of the PFCP association state.  Only the remote RTS is optional.

Change-Id: Ic766ebc56ce1810fc27cb49ddcac50bc94e539d9
Related: osmo-ttcn3-hacks.git I332e95a32935e0c8da99e600e8d30af14bd36b16
Vadim Yanitskiy at
gtpu_kpi: use ?ENV_DEFAULT_GTPU_KPI_{TABLE_NAME,INTERVAL}

Change-Id: Ia8194966b20da1adc26c02f416b8ec0508191d98
Vadim Yanitskiy at
config/sys.config: group pfcp_peer params into a map

Following the same pattern as sctp_{client,server}, group the flat
pfcp_loc_addr/pfcp_rem_addr environment variables into a pfcp_peer
map.  The old flat keys are still supported for backwards compat.

Changes:
* osmo_s1gw_sup: add pfcp_cfg(), merging legacy flat keys with the new
  pfcp_peer map (new takes priority); store the resolved config back via
  set_env(pfcp_peer, ...) so all consumers see a single canonical map
* pfcp_peer: change start_link/2 to start_link/1 taking a cfg() map;
  simplify init() using sctp_common:parse_addr/1; add cfg() type
* rest_server: read pfcp laddr/raddr from the pfcp_peer map

Change-Id: Iba954746fe20e6b9eeaec3196e1f83e3fc3e7fc2
Vadim Yanitskiy at
pfcp_peer: make assoc_setup and heartbeat_req timeouts configurable

Add assoc_setup_timeout and heartbeat_req_timeout as optional fields
in the pfcp_peer config map, with 2000 ms defaults.  Store the full
cfg() map in #peer_state{} and read values from it with maps:get/3
at the point of use.

Change-Id: I58a472a3bbbbad029a2f0246b084428ab3b1905c
Vadim Yanitskiy at

#757 (Mar 20, 2026, 9:31:08 PM)

contrib/jenkins_manuals: fix doc/manuals paths

Change-Id: Ibfccefec7e44a4803d4ff8ad061db395fc498b89
Oliver Smith at
[REST] Add MME source address/port to EnbItem

The enb_proxy now captures the local address and port of the S1GW-MME
SCTP connection (mme_saddr/mme_sport) at comm_up and includes them in
conn_info().  Expose this info through the REST API (EnbItem schema),
show it as a new column/row in the CLI (enb_list/enb_info).

Change-Id: I15bbddf96ac7d5b6f9962a8d745db58fdec334e7
Related: SYS#7066
Vadim Yanitskiy at
[REST] Add PLMN/eNB IDs to EnbItem

Change-Id: I79690223a34afea5e6661125ac04f98462dadb03
Related: SYS#7066
Vadim Yanitskiy at
doc/manuals: cli: shorten enb_list example table

The full enb_list table with all address columns is too wide to fit
on a page and does not render well in PDF.  Collapse the address
columns with '...'; add a note that they are omitted for readability.

Change-Id: I4e25233a3e77358060f1098e97907c93deab334b
Related: OS#6671
Vadim Yanitskiy at
enb_{proxy,registry}: signal MME conn info on SCTP comm_up

Previously, mme_aid/mme_saddr/mme_sport were only signalled to the
enb_registry once the S1 Setup procedure completed.  This meant the
REST API could not show MME connection details for eNBs stuck in
wait_s1setup_rsp state (e.g. due to a slow or retrying MME).

Add notify_mme_comm_up/2, called at SCTP comm_up, which stores the full
conn_info (including mme_aid, mme_saddr, mme_sport) in the registry as
soon as the SCTP connection is established.

notify_mme_connected/2 is simplified to notify_mme_connected/1: it now
only flips the state to 'connected', since conn_info is already stored.

Change-Id: Iea9ba4fdf961e6cd262edc154884a2eee3d95355
Related: SYS#7066
Vadim Yanitskiy at
enb_proxy: split conn_info() into mme_conn_info() and proxy_info()

The old conn_info() conflated two distinct concerns: the MME SCTP
connection info stored in enb_registry (aid, saddr, sport) and the
broader operational state used for introspection (handler pid, enb
connection info, etc.).  Mixing them forced enb_registry to hold a
handler pid it has no business knowing about, and required rest_server
to extract that pid just to reach s1ap_proxy for E-RAB listing.

Split into two distinct types:

* mme_conn_info() - pure MME SCTP connection info (aid, saddr, sport),
  stored in the enb_registry and signalled via notify_mme_comm_up/2.
  The `mme_` prefix is dropped from field names as the type name
  provides the context.

* proxy_info() - richer operational snapshot (handler, enb_handle,
  enb_conn_info, mme_conn_info, genb_id_str, mme_info), returned by
  fetch_info/1 for introspection/debugging purposes.

Additionally:

* Add fetch_erab_list/1 to enb_proxy, delegating internally to
  s1ap_proxy:fetch_erab_list/1 via the cached handler pid.  This
  allows the rest_server to obtain a list of E-RAB without having
  to obtain pid of the s1ap_proxy and interact with it.

* Remove separate enb_aid/mme_aid/mme_saddr/mme_sport state fields;
  enb_aid is now read directly from enb_conn_info, and the MME fields
  are grouped in mme_conn_info.

Change-Id: Ia428ceb4762f972211e9b790688dc89fb5b8a274
Related: SYS#7066
Vadim Yanitskiy at
enb_proxy: add missing mme_info() to proxy_info()

As a bonus, `tried_mmes` now only serves its actual purpose - tracking
which MMEs have already been tried for selection filtering - rather
than being abused as a way to retrieve the current MME name.

Change-Id: Ibbb293d9e68b7c3a8c3ca7ee73132dbda3f2bf97
Related: SYS#7066
Vadim Yanitskiy at
s1ap_proxy: add public erab_list() type

Change-Id: I21a674537d1d9f16fdb319aaf2d758ba0906b407
Vadim Yanitskiy at
rest_server: fix TOC/TOU race when listing/fetching E-RABs

The list of E-RAB FSM pids is a snapshot taken at one point in time.
By the time we interrogate each erab_fsm process individually, any of
them may have already terminated (e.g. bearer released mid-request).
The current code fails to generate a response if this happens.

* fetch_erab_info/1: add a pid() clause that wraps erab_list_item/1
  in a try/catch, returning 'error' if the process is gone.

* fetch_erab_info/1: catch both exit forms
** `{noproc, _}` raised by gen_statem:call/2 on a monitored pid, and
** the bare noproc atom for other code paths.

* fetch_erab_list/1: switch from lists:map to lists:filtermap and
  call fetch_erab_info/1 per E-RAB, silently dropping any that died
  between the snapshot and the per-process interrogation.

Change-Id: I160b413aa535f2379ad4e40a3ae8f37c5bce2067
Related: SYS#7066
Vadim Yanitskiy at
rest_server: log received requests (as debug)

Change-Id: I25da9662fb98a0eafcedfde21b12a937225f5fb9
Related: SYS#7066
Vadim Yanitskiy at

#755 (Mar 20, 2026, 11:37:07 AM)

contrib/jenkins_manuals: fix doc/manuals paths

Change-Id: Ibfccefec7e44a4803d4ff8ad061db395fc498b89
Oliver Smith at

#754 (Mar 20, 2026, 11:34:41 AM)

contrib/jenkins_manuals: remove old symlinks

In master-builds, the job runs without wiping the workspace. Remove
previous build and common symlinks, so the manuals job doesn't fail
with:

  + make -C doc/manuals
  make: Entering directory '/build/doc/manuals'
  ln -s /opt/osmo-gsm-manuals/build build
  ln: failed to create symbolic link 'build': File exists

Change-Id: I88add52dc5a4671cf4dcfd498c3103023a35b7ba
Oliver Smith at

#750 (Mar 19, 2026, 6:36:09 PM)

doc/manuals: merge doc/osmo-s1gw-cli.md

Change-Id: Ic2556f6add9c6a24f6da03f4388d7f86dde0c5dc
Related: OS#6671, SYS#7066
Vadim Yanitskiy at
doc/manuals: update MmeItem related info

The osmo-s1gw-cli documentation was not properly updated in 63ce2c9:

* default local/bind address for an MME is "any", not "::"
* allowed TACs (--tac) was missing in help and examples

Change-Id: I0b8115fe6342a80bf1eb99bd6ad210492a013947
Fixes: 63ce2c9 ("[REST] Add MmeList, MmeAdd, MmeInfo, MmeDelete")
Related: OS#6671, SYS#7066, SYS#7052
Vadim Yanitskiy at
contrib: add jenkins_manuals.sh

Change-Id: I54f0774575534519a2c40e0588f136c8a5e706f8
Related: osmo-ci.git Id62d806a648c8f3480cb4f162adf65f77c552848
Related: OS#6671
Vadim Yanitskiy at
rebar.lock: bump exometer_report_statsd version

The new version is using ETS instead of dict for counter lookups.
This significantly reduces performance impact when multiple eNBs
are registered, since the ETS provides O(1) average-case hash
lookups and in-place mutation.

Change-Id: I931321a831215012aa8186d851d9a8d38908a4bf
Vadim Yanitskiy at
config/sys.config: increase StatsD reporter interval to 10s

When running ttcn3-s1gw-test locally, I noticed OsmoS1GW consuming
30-40% of a CPU core while idle (not serving any eNBs).  Profiling
with etop pointed to `exometer_report_statsd` as the culprit: with
~1720 metrics registered, it was sending ~1720 datagrams per second.

A 1-second reporting interval causes noticeable CPU overhead as the
number of active metrics grows.  With per-eNB and per-MME counters,
the metric set scales with the number of connected eNBs/MMEs.

Increase the default StatsD reporting interval from 1s to 10s to
reduce that overhead.  Update the documentation accordingly.

Change-Id: Icd5e1a43d1df4a4909fe742aec67cc51a01bb857
Vadim Yanitskiy at
pfcp_peer: define ?PFCP_ASSOC_SETUP_TIMEOUT

Change-Id: I0b7299d9f3eaa8c0a3c7e4a4d6331d1ec5a1c779
Vadim Yanitskiy at
enb_proxy: signal mme_info() to the enb_registry

When an MME is selected from the pool, pass the mme_registry:mme_info()
to the enb_registry via notify_mme_connecting/2 (replacing /1).  This
makes all MME configuration details (name, address, port, TAC list)
available to consumers such as the REST server, without having to
look them up separately from the mme_registry.

Change-Id: I22e705b8196c9dcf436ed3c4d91c5c5a912e7282
Related: SYS#7052
Vadim Yanitskiy at
enb_proxy: obtain sctp_client sockopts from the env directly

Instead of passing the MmeConnCfg (sctp_client:cfg()) all the way
from osmo_s1gw_sup through sctp_server (as priv) into enb_proxy
(as state), read the sctp_client configuration in-place via
osmo_s1gw:get_env/2 when it is actually needed (connecting/enter).

This works because osmo_s1gw_sup already normalizes and writes back
the complete sctp_client config to the application env (set_env/2)
before starting the supervision tree.

As a result, mme_conn_cfg is removed from enb_proxy's state record,
start_link/2 no longer uses its Priv argument, and server_cfg/1 is
simplified to server_cfg/0.

Change-Id: Ic77d3eb3351c8981c87fa4b6febcdeb814b9187b
Vadim Yanitskiy at
[REST] Add GET /config endpoint (ConfigRead)

This endpoint returns the effective runtime configuration that
OsmoS1GW is currently using, with all defaults applied.  This
reflects the values read via `osmo_s1gw:get_env/2` at startup.

Change-Id: Ic6c9562a541e4a0728257538887537aac6b99b97
Related: SYS#7066
Vadim Yanitskiy at
[REST] Implement eNB/MME selection by addr-port

Add support for selecting an MME or eNB by remote address and port
in the REST API and CLI.  The selector format is `addr:IP:PORT` for
MMEs and `enb-conn:IP:PORT` for eNBs, where IP can be an IPv4 or
IPv6 address.  The colon is used as the address/port separator.

Change-Id: If02c8de1e1b7214bba868eee35233a79d0704dc5
Related: SYS#7066
Vadim Yanitskiy at
[REST] Implement PfcpAssoc{Setup,Release}

Change-Id: I2e24544563e4c4d23bb3d8a4a7b5434191b482d8
Related: SYS#7066
Vadim Yanitskiy at
[REST] EnbItem: expose name of the selected MME

For each eNB connection, include the name of the MME that was selected
from the pool.  Update the OpenAPI spec, CLI (enb_list/enb_info tables),
and user manual accordingly.

Change-Id: I4839275efa5d3545e84d684ad1b8b989214ef76a
Related: SYS#7066, SYS#7052
Vadim Yanitskiy at

#748 (Mar 18, 2026, 10:16:08 AM)

doc/manuals: add overview, document running and configuration

Change-Id: I4ceca069866d7191ef2b153af95a20cb522bffeb
Related: OS#6671
Vadim Yanitskiy at
doc/manuals: document the metrics

Change-Id: Iacfefd387d0cd26eebbbeba0cd37efa78f90bb46
Related: OS#6671, SYS#7065
Vadim Yanitskiy at
doc/manuals: document GTP-U KPI monitoring

Change-Id: I2709cd545bfd6c8f6e34358caf9d372c02dd5c3e
Related: OS#6671, SYS#7307
Vadim Yanitskiy at
doc/manuals: document the REST interface

Change-Id: I8bc9183fff8f65db71554ee26369db9bdb61b78a
Related: OS#6671, SYS#7066
Vadim Yanitskiy at

#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
Vadim Yanitskiy at
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
Vadim Yanitskiy at
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
Vadim Yanitskiy at
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
Vadim Yanitskiy at