open5gs/docs/_docs/platform/02-centos.md
Sukchan Lee 6a479ebae3
Some checks failed
Meson Continuous Integration / Build and Test on Ubuntu Latest (push) Has been cancelled
[mme] Follow-up #4693: harden DNS-based gateway selection
Follow up the DNS-based SGW/PGW selection introduced by #4693 and
harden several fallback, asynchronous completion, cache, resolver,
and compatibility paths found during review and testing.

A DNS failure must never prevent an attach when a statically
configured gateway remains available. Preserve the SGW serving the
UE when a DNS resolution is created and restore it whenever a later
DNS-based SGW selection cannot complete.

This is required because a previous Create Session Request attempt
may already have switched the UE to a DNS-discovered SGW. If that
request times out and the next DNS candidate cannot be resolved, simply
returning from apply_sgw() leaves the UE attached to the failed DNS SGW
and causes subsequent CSR attempts to continue using it instead of the
original fallback SGW.

Restore the pre-DNS SGW when:

* the SGW DNS leg ends in fallback after an earlier SGW switch
* mme_sgw_add() cannot create the selected SGW node
* ogs_gtp_connect() cannot connect the selected SGW
* the deferred Create Session Request cannot be built or committed

The fallback pointer records the SGW currently serving the UE when the
resolution is created rather than assuming it is always statically
configured. Successfully connected SGW nodes are retained for the
process lifetime, so the stored pointer remains valid.

The PGW path does not require the same state restoration. When PGW DNS
selection falls back, mme_dns_sess_pgw_addr() returns NULL and the S11
Create Session Request builder selects the statically configured PGW
again on each attempt.

Harden deferred CSR completion handling.

post_resolved() runs on the MME main thread, which is also responsible
for draining the application event queue. ogs_queue_push() can block
indefinitely when that queue is full, causing a self-deadlock because
the blocked producer is also the only consumer.

Use ogs_queue_trypush() instead. When it returns OGS_RETRY, return the
resolution to PENDING and restart its guard timer. The DNS legs are
already complete, so the guard callback simply attempts to post the
completed result again. Do not retry when the queue has been terminated
during shutdown.

Do not move a resolution to CONSUMED or increment csr_attempts until
the deferred CSR transaction has been successfully built and committed.
The GTP timeout retry path uses CONSUMED as its eligibility condition,
so marking an unsent request as consumed leaves meaningless retry state.

If local CSR creation fails, restore the pre-DNS SGW first and then
remove the resolution. This prevents a subsequent NAS retry from
capturing the failed DNS SGW as its new fallback and prevents a stale
CONSUMED resolution from incorrectly short-circuiting the new request
to SEND_NOW.

Document that OGS_OK from mme_gtp_send_create_session_request() may
mean that the request was deferred for DNS selection and does not
guarantee that a GTP transaction exists when the function returns.

Fix an MME event-loop hang caused by unusable SRV answers.

An SRV record whose target is "." indicates that the service is not
available. mme_dns_candidate_apply_srv() previously left the candidate
unchanged in this case. leg_advance() then repeatedly read the same
cached SRV answer without advancing the candidate cursor, hanging the
MME event loop.

Make mme_dns_candidate_apply_srv() return whether it found a usable
target. Skip the candidate when all SRV targets are empty or ".", while
leaving the candidate unchanged for callers that need to inspect the
failure.

Add unit and DNS integration coverage for this case. Extend the test DNS
server to encode "." as the DNS root label and verify that the MME skips
the unusable SRV candidate and falls back instead of hanging.

Bound the MME DNS cache to prevent expired entries from accumulating
indefinitely across many TAC and APN names.

When inserting a new key at capacity:

* remove expired entries
* if the cache remains full, evict entries closest to expiry
* do not make room when replacing an existing key, which would otherwise
  evict an unrelated entry on every refresh at capacity

Disable the c-ares internal query cache when the installed c-ares
version provides ARES_OPT_QUERY_CACHE. c-ares 1.31 and later enable an
internal cache by default, which can continue answering a query after
the MME's own cache entry has expired and silently override the
operator-configured dns.cache_ttl. Set qcache_max_ttl to zero so the
MME cache remains authoritative.

Retain compatibility with both older distribution versions of c-ares
and newer releases that deprecate the legacy query and reply parsing
APIs. Define CARES_NO_DEPRECATED before including ares.h to suppress
the newer deprecation attributes without raising the minimum supported
c-ares version.

Validate configured resolver addresses before initializing c-ares.

Require each dns.server address to be a bare IPv4 or IPv6 literal.
Reject hostnames, malformed addresses, bracketed addresses, scope
suffixes, and IPv6 link-local addresses, which require interface scope
handling that is not supported by the MME configuration.

Format resolver entries as:

* IPv4: address:port
* IPv6: [address]:port

The brackets are required by the c-ares server CSV format. Without
them, a value such as 2001:db8::53:53 is accepted as a different valid
IPv6 address using the default DNS port, causing queries to be sent
silently to the wrong resolver.

Build and validate the server CSV before c-ares initialization so
configuration failures do not require partially initialized channel or
library cleanup. The fixed CSV buffer remains safe because inet_pton()
guarantees that accepted addresses fit the maximum IPv4 or IPv6 literal
length.

Explicitly initialize mme_sess_t::dns_id to OGS_INVALID_POOL_ID instead
of relying on the current value of the pool allocator's zero-filled
memory.

Update the sample configuration and documentation to describe the
current DNS-selection limitations:

* roaming PGW lookup currently builds the APN-FQDN from the serving PLMN
  rather than deriving the home-PLMN APN-OI
* only A records are used for discovered gateways
* SRV weights are ignored
* non-terminal NAPTR records are not followed
* DNS-discovered SGW nodes are retained for the process lifetime
* resolver addresses must be bare IPv4 or IPv6 literals
* link-local IPv6 resolvers are not supported

Add the c-ares development dependency to Debian, Ubuntu, Fedora, Alpine,
CentOS, macOS, and FreeBSD build instructions and container images, and
add the c-ares MIT license notice.

Also link the DNS selection unit tests against libmme directly so they
exercise the same mme-dns-select implementation built for the MME.
2026-07-21 16:44:07 +09:00

9.6 KiB

title head_inline
CentOS <style> .blue { color: blue; } </style>

This guide is based on CentOS Stream 8 Distribution. {: .blue}

Install CentOS Stream 8 from Vagrant box (optional)


Vagrant provides a simple way to create and deploy Virtual Machines from pre-built images using VirtualBox, libvirt, or VMWare as a hypervisor engine. This allows the user to quickly create a virtual machine without the hassle of installing the operating system by hand.

Install Vagrant


The instructions to install Vagrant are provided at vagrantup.com.

Create a CentOS Stream 8 Virtual Machine using Vagrant


Use the supplied Vagrantfile in the vagrant directory to create the virtual machine.

Note that this Vagrantfile is identical to the base CentOS Stream 8 box, with the exception that the amount of virtual memory has been increased to 1GB:

cd vagrant/centos
vagrant up --provider virtualbox

Log into the newly created CentOS VM


Use SSH to log into the CentOS Stream 8 VM:

vagrant ssh

Note that the Open5GS source is not copied into the VM. The instructions below provide the step by step instructions for setting up Open5GS for either a bare metal or virtual CentOS Stream 8 system.

The rest of the commands below are performed inside the CentOS VM as the user 'vagrant', or on your bare metal CentOS Stream 8 system as any normal user.

Install prerequisite packages to build and run Open5GS


Enable CentOS Stream 8 PowerTools repository


$ sudo dnf install 'dnf-command(config-manager)'
$ sudo dnf config-manager --set-enabled powertools

Enable the Extra Packages for Enterprise Linux


Enable the Extra Packages for Enterprise Linux repo by installing the epel-release package:

$ sudo dnf install epel-release

Enable ELRepo


Enable the ELRepo repo (with testing enabled):

$ sudo dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm
$ sudo dnf config-manager --set-enabled elrepo-testing

Install MongoDB using the package manager:


Tip: MongoDB is used as the database for PCF/UDR and PCRF/HSS. {: .notice--info}

Note: If you use an external MongoDB server, you can skip this section. {: .notice--warning}

Create a repository file to install the MongoDB packages:

$ sudo sh -c 'cat << EOF > /etc/yum.repos.d/mongodb-org-6.0.repo
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/\$releasever/mongodb-org/6.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc
EOF'

Install MongoDB using the package manager:

$ sudo dnf -y install mongodb-org

Install the dependencies for building the source code.


Open5GS requires several packages which are not installed by default in a base CentOS Stream 8 installation.

$ sudo dnf install python3 meson cmake ninja-build gcc gcc-c++ flex bison git cmake lksctp-tools-devel libidn-devel c-ares-devel gnutls-devel libgcrypt-devel openssl-devel cyrus-sasl-devel libyaml-devel mongo-c-driver-devel libmicrohttpd-devel libcurl-devel libnghttp2-devel libtalloc-devel

Install iproute IP interface tools.


$ sudo dnf install iproute

Install the SCTP kernel module in kernel-modules-extra.


$ sudo dnf install kernel-modules-extra
$ sudo rm /etc/modprobe.d/sctp-blacklist.conf
$ sudo rm /etc/modprobe.d/sctp_diag-blacklist.conf

Update all installed packages to the latest versions.


This will update all of the installed packages to the latest versions from all of the repos that we enabled above.

$ sudo dnf update

Note that this may update the kernel version so you may need to reboot after this step to ensure that you are running this new kernel version. This is important when you try to load the SCTP kernel module later.

[host] $ vagrant halt
[host] $ vagrant up --provider virtualbox
[host] $ # ssh back into the VM after it reboots...
[host] $ vagrant ssh

Check the SCTP kernel module


Open5GS requires the Linux SCTP kernel module to be loaded in the kernel. In the CentOS Stream 8 Vagrant box SCTP is not loaded into the kernel automatically so must be installed as follows:

$ checksctp
SCTP supported
$ sudo modprobe sctp
$ # Check that SCTP was loaded successfully:
$ sudo dmesg | grep sctp
[  639.971360] sctp: Hash tables configured (bind 256/256)

Build Open5GS from Source


Git clone the Open5GS source code.


Clone a copy of the open5GS source code from github:

$ cd ~
$ git clone https://github.com/{{ site.github_username }}/open5gs

Compile the Open5GS source.


Compile the source using meson. Note that this sets the installation prefix to ~/open5gs/install:

$ cd ~/open5gs
$ meson build --prefix=`pwd`/install
$ ninja -C build

Run and Test Open5GS


Start MongoDB


After installing the MongoDB server, it will be started automatically when the machine is rebooted. You can check the status using systemctl:

$ sudo systemctl status mongod.service
● mongod.service - MongoDB Database Server
   Loaded: loaded (/usr/lib/systemd/system/mongod.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2020-11-20 09:46:40 UTC; 10h ago
     Docs: https://docs.mongodb.org/manual
  Process: 779 ExecStart=/usr/bin/mongod $OPTIONS (code=exited, status=0/SUCCESS)
  Process: 775 ExecStartPre=/usr/bin/chmod 0755 /var/run/mongodb (code=exited, status=0/SUCCESS)
  Process: 770 ExecStartPre=/usr/bin/chown mongod:mongod /var/run/mongodb (code=exited, status=0/SUCCESS)
  Process: 732 ExecStartPre=/usr/bin/mkdir -p /var/run/mongodb (code=exited, status=0/SUCCESS)
 Main PID: 781 (mongod)
   Memory: 99.7M
   CGroup: /system.slice/mongod.service
           └─781 /usr/bin/mongod -f /etc/mongod.conf

If it has not started, then it can be started using systemctl:

$ sudo systemctl enable mongod.service
$ sudo systemctl start mongod.service

The default database location is /var/log/mongodb/mongod.log. This can be adjusted in /etc/mongod.conf.

Set up a TUN interface


Create a TUN/TAP interface. The interface name will be ogstun.

$ sudo ip tuntap add name ogstun mode tun
$ ip link show

To support IPv6-enabled UEs, you must configure the ogstun interface to support IPv6. This is done by setting the diable_ipv6 option for ogstun to 0 (false):

$ sysctl -n net.ipv6.conf.lo.disable_ipv6
1
$ sysctl -n net.ipv6.conf.ogstun.disable_ipv6
1

$ sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
$ sudo sysctl -w net.ipv6.conf.ogstun.disable_ipv6=0

$ sysctl -n net.ipv6.conf.lo.disable_ipv6
0
$ sysctl -n net.ipv6.conf.ogstun.disable_ipv6
0

Note: If your TUN interface already supports IPv6, you can skip this steps above. {: .notice--info}

Set the IP address on the ogstun TUN interface.

$ sudo ip addr add 10.45.0.1/16 dev ogstun
$ sudo ip addr add 2001:db8:cafe::1/48 dev ogstun

Make sure it is set up properly.

$ sudo ip link set ogstun up
$ ip link show

Notice: This configuration is not persistent after rebooting. The script provided in [$GIT_REPO/misc/netconf.sh](https://github.com/{{ site.github_username }}/open5gs/blob/main/misc/netconf.sh) makes it easy to configure the TUN device as follows: $ sudo ./misc/netconf.sh {: .notice--info}

Testing Open5GS


Run Individual Open5GS tests.


Open5GS test applications are created in the ~/open5gs/build/tests directory.

$ cd ~/open5gs

$ ./build/tests/attach/attach ## EPC Only
s1setup-test        : SUCCESS
guti-test           : SUCCESS
auth-test           : SUCCESS
idle-test           : SUCCESS
emm-status-test     : SUCCESS
ue-context-test     : SUCCESS
reset-test          : SUCCESS
All tests passed.

$ ./build/tests/registration/registration ## 5G Core Only
guti-test           : SUCCESS
auth-test           : SUCCESS
idle-test           : SUCCESS
dereg-test          : SUCCESS
identity-test       : SUCCESS
gmm-status-test     : SUCCESS
ue-context-test     : SUCCESS
All tests passed.

Run all Open5GS tests.


Run all Open5GS test programs:

$ cd ~/open5gs
$ cd build
$ meson test -v

Tip: You can also check the result of meson test -v with a tool that captures packets. If you are running wireshark, select the loopback interface and set FILTER to s1ap || gtpv2 || pfcp || diameter || gtp || ngap || http. You can see the virtually created packets. [testattach.pcapng]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testattach.pcapng)/[testregistration.pcapng]({{ site.url }}{{ site.baseurl }}/assets/pcapng/testregistration.pcapng) {: .notice--info}

Installing Open5GS.


Open5GS can be installed using meson as well. This will install the product to $(HOME)/open5gs/install if the prefix was set as shown above.

$ cd ~/open5gs
$ cd build
$ ninja install
$ cd ../
$ ls install/bin
open5gs-amfd   open5gs-mmed   open5gs-pcrfd  open5gs-sgwud  open5gs-upfd
open5gs-ausfd  open5gs-nrfd   open5gs-scpd   open5gs-smfd
open5gs-bsfd   open5gs-nssfd  open5gs-seppd  open5gs-udmd
open5gs-hssd   open5gs-pcfd   open5gs-sgwcd  open5gs-udrd

Building WebUI of Open5GS


Node.js is required to build WebUI of Open5GS

Install Node.js:

$ curl -sL https://rpm.nodesource.com/setup_18.x | sudo -E bash -
$ sudo dnf install nodejs

Install the dependencies to run WebUI

$ cd ~/open5gs
$ cd webui
$ npm ci

The WebUI runs as an npm script.

$ DB_URI=mongodb://127.0.0.1/open5gs HOSTNAME=0.0.0.0 npm run dev