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.
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