CI: enable PCF-based dual-DNN multi-QoS in 25PRB RFSim

Expand the 25PRB RFSim CI scenario to validate two PDU sessions with
PCF-driven QoS rules and deterministic per-flow traffic ports. Update
iperf execution to use `-B`/`-p` from test args.

Changes:
- Update `container_5g_rfsim_u0_25prb.xml` to add dual-session
  validation steps, routing setup, and multi-flow UDP iperf
  for UL/DL with explicit bind/port arguments.
- Change `cls_oaicitest.py` to parse bind/port from
  `iperf_args`, and return a clear error when the iperf client exits non-zero.
- Update `nrue.uicc.2pdu.conf` to configure
  session 2 on `openairinterface` with distinct `nssai_sd` values.
- Extend `5g_rfsimulator/mini_nonrf_config.yaml`
  with PCF endpoints, dual slices, dual DNN entries, and PCF policy paths,
  and disable local PCC rules in SMF.
- Update `5g_rfsimulator_u0_25prb/docker-compose.yaml`
  to add the `oai-pcf` service, mount policy directories, and add ext-dn
  route for the second UE subnet.
- Add PCF policy data files for this scenario, in :
  `policies/policy_decisions/policy_decision.yaml`
  `policies/pcc_rules/pcc_rules.yaml`
  `policies/qos_data/qos_data.yaml`

About the ci-scripts:

Co-authored-by: Robert Schmidt <robert.schmidt@openairinterface.org>

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
This commit is contained in:
Guido Casati
2026-04-21 11:20:32 +02:00
parent 5a1af1a7db
commit f02c84fcfc
9 changed files with 290 additions and 35 deletions

View File

@@ -65,6 +65,23 @@ def Iperf_ComputeTime(args):
raise Exception('Iperf time not found!')
return int(result.group('iperf_time'))
def Iperf_UpdateBindPort(opts, ueIP, idx):
# search if bind address present. Extract if yes, add if no.
bind_m = re.search(r'(-B|--bind)\s+(?P<ip>\d+\.\d+\.\d+\.\d+)', opts)
if bind_m:
bindIP = bind_m.group('ip')
else:
bindIP = ueIP
opts += f" -B {ueIP}"
# search if port present. Extract if yes, add if no.
port_m = re.search(r'(-p|--port)\s+(?P<port>\d+)', opts)
if port_m:
port = port_m.group('port')
else:
port = 5002 + idx
opts += f" -p {port}"
return bindIP, port, opts
def convert_to_mbps(value, magnitude):
value = float(value)
if magnitude == 'K' or magnitude == 'k':
@@ -457,7 +474,6 @@ class OaiCiTest():
svrIP = cn.getIP()
if not svrIP:
return (False, f"Iperf server {cn.getName()} has no IP address")
iperf_opt = self.iperf_args
jsonReport = "--json"
serverReport = ""
@@ -471,11 +487,11 @@ class OaiCiTest():
# note: enable server report collection on the UE side, no need to store and collect server report separately on the server side
serverReport = "--get-server-output"
iperf_time = Iperf_ComputeTime(self.iperf_args)
bindIP, port, iperf_opt = Iperf_UpdateBindPort(iperf_opt, ueIP, idx)
# hack: the ADB UEs don't have iperf in $PATH, so we need to hardcode for the moment
iperf_ue = '/data/local/tmp/iperf3' if re.search('adb', ue.getName()) else 'iperf3'
ue_header = f'UE {ue.getName()} ({ueIP})'
ue_header = f'UE {ue.getName()} ({bindIP})'
with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(cn.getHost()) as cmd_svr:
port = 5002 + idx
# note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False`
t = iperf_time * 2.5
cmd_ue.run(f'rm {client_filename}', reportNonZero=False, silent=True)
@@ -486,7 +502,9 @@ class OaiCiTest():
if ret.returncode == 0:
logging.warning(f'Iperf3 server on port {port} detected and terminated')
cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} >> /dev/null &', timeout=t)
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t)
client_ret = cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -c {svrIP} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t, reportNonZero=False)
if client_ret.returncode != 0:
return (False, f'{ue_header}\nIperf client command failed on {bindIP} -> {svrIP}:{port} (return code: {client_ret.returncode})')
dest_filename = archiveArtifact(cmd_ue, ctx, client_filename)
if udpIperf:
status, msg = Iperf_analyzeV3UDP(dest_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)

View File

@@ -5,8 +5,8 @@ uicc0 = {
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
pdu_sessions = (
{ id = 1; dnn = "oai"; nssai_sst = 1; },
{ id = 2; dnn = "oai"; nssai_sst = 1; }
{ id = 1; dnn = "oai"; nssai_sst = 1; nssai_sd = 0xFFFFFF; },
{ id = 2; dnn = "openairinterface"; nssai_sst = 1; nssai_sd = 0x123456; }
)
}

View File

@@ -82,5 +82,17 @@ class TestPingIperf(unittest.TestCase):
success = self.ci.Iperf(self.ctx, self.node, self.html, infra_file=infra_file)
self.assertTrue(success)
def test_iperf_new_bindport(self):
self.ci.iperf_args = "-u -t 5 -b 22M -O 0 -p 10000 -B 127.0.0.3"
self.ci.svr_id = "test"
self.ci.svr_node = "localhost"
self.ci.iperf_packetloss_threshold = "0"
self.ci.iperf_bitrate_threshold = "0"
self.ci.iperf_profile = "balanced"
infra_file = "tests/config/infra_ping_iperf.yaml"
# TODO Should need nothing but options and UE(s) to use
success = self.ci.Iperf(self.ctx, self.node, self.html, infra_file=infra_file)
self.assertTrue(success)
if __name__ == '__main__':
unittest.main()

View File

@@ -23,7 +23,7 @@
<desc>Deploy OAI 5G CoreNetwork</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_u0_25prb</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
<services>mysql oai-amf oai-smf oai-pcf oai-upf oai-ext-dn</services>
</testCase>
<testCase>
@@ -41,28 +41,6 @@
<node>localhost</node>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping ext-dn from NR-UE</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>
<id>rfsim5g_ext_dn</id>
<node>localhost</node>
<svr_id>rfsim5g_ue</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Verify 2nd PDU session is up</desc>
@@ -71,14 +49,109 @@
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping ext-dn from NR-UE on PDU session ID 2</desc>
<class>Custom_Command</class>
<desc>Configure policy routing for PDU2 source IP</desc>
<may_fail>true</may_fail>
<node>localhost</node>
<command>docker exec rfsim5g-oai-nr-ue sh -c "ip rule add from 12.1.2.2/32 table 1002 prio 1002; ip route replace default dev oaitun_ue1p2 src 12.1.2.2 table 1002"</command>
</testCase>
<testCase>
<class>Custom_Command</class>
<desc>Ping ext-dn from UE PDU session 1 interface</desc>
<node>localhost</node>
<command>docker exec rfsim5g-oai-nr-ue ping -I oaitun_ue1 -c 10 192.168.72.135</command>
</testCase>
<testCase>
<class>Custom_Command</class>
<desc>Ping ext-dn from UE PDU session 2 interface</desc>
<node>localhost</node>
<command>docker exec rfsim5g-oai-nr-ue ping -I oaitun_ue1p2 -c 10 192.168.72.135</command>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) QFI 1 (default) flow on PDU session 1 (port 5201)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.1.2 -p 5201</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) QFI 2 flow on PDU session 1 (port 52080)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.1.2 -p 52080</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) QFI 3 flow on PDU session 1 (port 52081)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.1.2 -p 52081</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) default flow on PDU session 2 (port 5201)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.2.2 -p 5201</iperf_args>
<id>rfsim5g_ue_pdu_2</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/1Mbps/UDP)(6 sec) QFI 1 (default) flow on PDU session 1 (port 5201)</desc>
<iperf_args>-u -b 1M -t 6 -R -B 12.1.1.2 -p 5201</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/1Mbps/UDP)(6 sec) QFI 2 flow on PDU session 1 (port 52080)</desc>
<iperf_args>-u -b 1M -t 6 -R -B 12.1.1.2 -p 52080</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/1Mbps/UDP)(6 sec) QFI 3 flow on PDU session 1 (port 52081)</desc>
<iperf_args>-u -b 1M -t 6 -R -B 12.1.1.2 -p 52081</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>

View File

@@ -43,6 +43,12 @@ nfs:
n4:
interface_name: eth0
port: 8805
pcf:
host: oai-pcf
sbi:
port: 8080
api_version: v1
interface_name: eth0
upf:
host: oai-upf
@@ -77,6 +83,10 @@ database:
snssais:
- &embb_slice1
sst: 1
sd: "FFFFFF"
- &embb_slice2
sst: 1
sd: "123456"
############## NF-specific configuration
amf:
@@ -103,6 +113,7 @@ amf:
tac: 0x0001
nssai:
- *embb_slice1
- *embb_slice2
supported_integrity_algorithms:
- "NIA0"
- "NIA1"
@@ -116,7 +127,7 @@ smf:
ue_mtu: 1500
support_features:
use_local_subscription_info: yes # Use infos from local_subscription_info or from UDM
use_local_pcc_rules: yes # Use infos from local_pcc_rules or from PCF
use_local_pcc_rules: no # Enforce PCC rules from PCF
upfs:
- host: oai-upf
port: 8805
@@ -144,6 +155,9 @@ smf:
- sNssai: *embb_slice1
dnnSmfInfoList:
- dnn: "oai"
- sNssai: *embb_slice2
dnnSmfInfoList:
- dnn: "openairinterface"
local_subscription_infos:
- single_nssai: *embb_slice1
dnn: "oai"
@@ -151,6 +165,19 @@ smf:
5qi: 9
session_ambr_ul: "200Mbps"
session_ambr_dl: "400Mbps"
- single_nssai: *embb_slice2
dnn: "openairinterface"
qos_profile:
5qi: 9
session_ambr_ul: "200Mbps"
session_ambr_dl: "400Mbps"
pcf:
local_policy:
policy_decisions_path: /openair-pcf/policies/policy_decisions
pcc_rules_path: /openair-pcf/policies/pcc_rules
traffic_rules_path: /openair-pcf/policies/traffic_rules
qos_data_path: /openair-pcf/policies/qos_data
upf:
support_features:
@@ -162,9 +189,15 @@ upf:
- sNssai: *embb_slice1
dnnUpfInfoList:
- dnn: "oai"
- sNssai: *embb_slice2
dnnUpfInfoList:
- dnn: "openairinterface"
## DNN configuration
dnns:
- dnn: "oai"
pdu_session_type: "IPV4"
ipv4_subnet: "12.1.1.0/24"
- dnn: "openairinterface"
pdu_session_type: "IPV4"
ipv4_subnet: "12.1.2.0/24"

View File

@@ -45,9 +45,26 @@ services:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
- oai-pcf
networks:
public_net:
ipv4_address: 192.168.71.133
oai-pcf:
container_name: "rfsim5g-oai-pcf"
image: oaisoftwarealliance/oai-pcf:v2.2.1
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-pcf/etc/config.yaml
- ./policies/policy_decisions:/openair-pcf/policies/policy_decisions
- ./policies/pcc_rules:/openair-pcf/policies/pcc_rules
- ./policies/traffic_rules:/openair-pcf/policies/traffic_rules
- ./policies/qos_data:/openair-pcf/policies/qos_data
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.142
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.2.1
@@ -78,7 +95,8 @@ services:
init: true
entrypoint: /bin/bash -c \
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity"
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0;"\
"ip route add 12.1.2.0/24 via 192.168.72.134 dev eth0; sleep infinity"
depends_on:
- oai-upf
networks:

View File

@@ -0,0 +1,29 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
gbr-rule-default-3mbps:
flowInfos:
- flowDescription: permit out ip from any to assigned
packetFilterUsage: true
precedence: 10
refQosData:
- gbr-qos-default-3mbps
gbr-rule-52080:
flowInfos:
- flowDescription: permit out ip from any to assigned 52080
packetFilterUsage: true
- flowDescription: permit in ip from assigned 52080 to any
packetFilterUsage: true
precedence: 7
refQosData:
- gbr-qos-52080-20mbps
gbr-rule-52081:
flowInfos:
- flowDescription: permit out ip from any to assigned 52081
packetFilterUsage: true
- flowDescription: permit in ip from assigned 52081 to any
packetFilterUsage: true
precedence: 8
refQosData:
- gbr-qos-52081-10mbps

View File

@@ -0,0 +1,35 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
decision_default:
default: true
pcc_rules:
- gbr-rule-default-3mbps
decision_dnn_oai:
dnn: oai
pcc_rules:
- gbr-rule-default-3mbps
- gbr-rule-52080
- gbr-rule-52081
decision_dnn_openairinterface:
dnn: openairinterface
pcc_rules:
- gbr-rule-default-3mbps
decision_supi2:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059450
decision_supi3:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059451
decision_supi4:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059452
decision_supi5:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059453

View File

@@ -0,0 +1,37 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
gbr-qos-default-3mbps:
5qi: 9
maxbrUl: 1000 Mbps
maxbrDl: 1000 Mbps
gbrUl: 3 Mbps
gbrDl: 3 Mbps
arp:
priorityLevel: 8
preemptCap: NOT_PREEMPT
preemptVuln: NOT_PREEMPTABLE
priorityLevel: 50
gbr-qos-52080-20mbps:
5qi: 67
maxbrUl: 1000 Mbps
maxbrDl: 1000 Mbps
gbrUl: 20 Mbps
gbrDl: 20 Mbps
arp:
priorityLevel: 8
preemptCap: NOT_PREEMPT
preemptVuln: NOT_PREEMPTABLE
priorityLevel: 30
gbr-qos-52081-10mbps:
5qi: 69
maxbrUl: 1000 Mbps
maxbrDl: 1000 Mbps
gbrUl: 10 Mbps
gbrDl: 10 Mbps
arp:
priorityLevel: 8
preemptCap: NOT_PREEMPT
preemptVuln: NOT_PREEMPTABLE
priorityLevel: 40