[[gtpu_kpi]]
== GTP-U KPI Monitoring

OsmoS1GW includes an optional GTP-U KPI monitoring module that tracks
per-eNB GTP-U traffic volume.  Because OsmoS1GW does not carry GTP-U
traffic itself (see <<architecture>>), it cannot count packets and bytes
from the data path directly.  Instead, it leverages the Linux
https://netfilter.org/projects/nftables/[nftables] framework: when an
eNB connects and its GTP-U transport address becomes known, OsmoS1GW
dynamically installs nftables rules with named counters to measure the
traffic for that eNB.  The counters are polled at a configurable interval
and the results are exposed as exometer metrics.

This feature is disabled by default.  See <<config_gtpu_kpi>> for the
configuration parameters.

[[gtpu_kpi_requirements]]
=== Requirements

* Linux kernel 5.2 or later with nftables support.
* The `CAP_NET_ADMIN` capability is required to manipulate nftables
  rules.  This is already granted by the installed systemd unit (see
  <<running_systemd>>).

[[gtpu_kpi_how_it_works]]
=== How It Works

[[gtpu_kpi_nftables_setup]]
==== nftables Table and Chains

At startup, the GTP-U KPI module creates a dedicated nftables table (an
`inet` family table named `osmo-s1gw` by default, configurable via
`gtpu_kpi_table_name`).  The table is marked as process-owned, so the
kernel automatically removes it if OsmoS1GW terminates unexpectedly.  If
a table with the same name already exists from a previous run, it is
flushed first.

Inside this table the module creates two base chains:

`gtpu-ul` (hook: `prerouting`)::
  Counts uplink GTP-U traffic — packets whose source address matches a
  registered eNB's GTP-U address.

`gtpu-dl` (hook: `postrouting`)::
  Counts downlink GTP-U traffic — packets whose destination address
  matches a registered eNB's GTP-U address.

Both chains prepend two rules that fast-skip non-GTP-U packets (i.e.
anything that is not UDP or whose destination port is not 2152).

[[gtpu_kpi_address_learning]]
==== GTP-U Address Learning

GTP-U transport addresses are not explicitly configured; they are learned
at run time from the S1AP signalling exchanged during E-RAB establishment.
The source of the address used for nftables rule matching is controlled by
the `gtpu_kpi_ul_addr` and `gtpu_kpi_dl_addr` configuration parameters:

`s1ap` (default)::
  The GTP-U address is taken from the F-TEID IEs carried in S1AP PDUs
  (E-RAB Setup, Initial Context Setup, etc.).

`sctp`::
  The GTP-U address is assumed to be the same as the eNB's SCTP source
  address.  Useful when the GTP-U and S1AP addresses are always the same.

[[gtpu_kpi_per_enb_rules]]
==== Per-eNB Counter Rules

Once an eNB's GTP-U address is known, the module adds:

* A named nftables counter (`ul-{GlobalENBId}` for uplink,
  `dl-{GlobalENBId}` for downlink).
* A rule in the appropriate chain that matches packets by source (UL) or
  destination (DL) IP address and updates the corresponding counter.

When an eNB disconnects, the matching nftables rule is removed, but the
counter object is intentionally left in place.  This means counters
accumulate across reconnections of the same eNB and do not reset to zero
if the eNB briefly drops and re-establishes its connection.

[[gtpu_kpi_polling]]
==== Counter Polling and Metric Reporting

A periodic timer (interval configurable via `gtpu_kpi_interval`, default
3000 ms) fires and reads all named counters from the nftables table.  For
each registered eNB the difference since the last poll is computed and
added to the corresponding exometer counters.

Three metrics are maintained per eNB per direction (see also
<<metrics_per_enb_counters>>):

`enb.{id}.gtpu.packets.{ul|dl}`::
  Total number of GTP-U packets seen for this eNB.

`enb.{id}.gtpu.bytes.total.{ul|dl}`::
  Total bytes seen for this eNB, including the outer IP, UDP, and GTP-U
  headers (i.e. what nftables reports).

`enb.{id}.gtpu.bytes.ue.{ul|dl}`::
  Estimated UE payload bytes — total bytes minus the fixed overhead of
  the outer IP header (20 bytes), UDP header (8 bytes), and GTP-U header
  (8 bytes), i.e. 36 bytes per packet.

NOTE: The UE payload byte estimate assumes a fixed 20-byte IP header.
      IPv6 or IP options would result in a slight overcount in the
      overhead subtraction.

// vim:set ts=4 sw=4 et:
