From f02c84fcfc6b81b5d0790108fb4daec468f5e9aa Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 21 Apr 2026 11:20:32 +0200 Subject: [PATCH] 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 Signed-off-by: Guido Casati --- ci-scripts/cls_oaicitest.py | 26 +++- ci-scripts/conf_files/nrue.uicc.2pdu.conf | 4 +- ci-scripts/tests/ping-iperf.py | 12 ++ .../xml_files/container_5g_rfsim_u0_25prb.xml | 127 ++++++++++++++---- .../5g_rfsimulator/mini_nonrf_config.yaml | 35 ++++- .../docker-compose.yaml | 20 ++- .../policies/pcc_rules/pcc_rules.yaml | 29 ++++ .../policy_decisions/policy_decision.yaml | 35 +++++ .../policies/qos_data/qos_data.yaml | 37 +++++ 9 files changed, 290 insertions(+), 35 deletions(-) create mode 100644 ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/pcc_rules/pcc_rules.yaml create mode 100644 ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/policy_decisions/policy_decision.yaml create mode 100644 ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/qos_data/qos_data.yaml diff --git a/ci-scripts/cls_oaicitest.py b/ci-scripts/cls_oaicitest.py index 50e1b73750..817a08f8fd 100644 --- a/ci-scripts/cls_oaicitest.py +++ b/ci-scripts/cls_oaicitest.py @@ -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\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\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) diff --git a/ci-scripts/conf_files/nrue.uicc.2pdu.conf b/ci-scripts/conf_files/nrue.uicc.2pdu.conf index 724d8edd41..6eb67510f3 100644 --- a/ci-scripts/conf_files/nrue.uicc.2pdu.conf +++ b/ci-scripts/conf_files/nrue.uicc.2pdu.conf @@ -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; } ) } diff --git a/ci-scripts/tests/ping-iperf.py b/ci-scripts/tests/ping-iperf.py index c642b9a923..e82dbbb0da 100644 --- a/ci-scripts/tests/ping-iperf.py +++ b/ci-scripts/tests/ping-iperf.py @@ -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() diff --git a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml index 362bb36431..af6b163b8c 100644 --- a/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml +++ b/ci-scripts/xml_files/container_5g_rfsim_u0_25prb.xml @@ -23,7 +23,7 @@ Deploy OAI 5G CoreNetwork localhost ci-scripts/yaml_files/5g_rfsimulator_u0_25prb - mysql oai-amf oai-smf oai-upf oai-ext-dn + mysql oai-amf oai-smf oai-pcf oai-upf oai-ext-dn @@ -41,28 +41,6 @@ localhost - - Ping - Ping ext-dn from NR-UE - rfsim5g_ue - localhost - rfsim5g_ext_dn - localhost - -c 20 -i 0.25 - 5 - - - - Ping - Ping NR-UE from ext-dn - rfsim5g_ext_dn - localhost - rfsim5g_ue - localhost - -c 20 -i 0.25 - 5 - - Attach_UE Verify 2nd PDU session is up @@ -71,14 +49,109 @@ - Ping - Ping ext-dn from NR-UE on PDU session ID 2 + Custom_Command + Configure policy routing for PDU2 source IP + true + localhost + 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" + + + + Custom_Command + Ping ext-dn from UE PDU session 1 interface + localhost + docker exec rfsim5g-oai-nr-ue ping -I oaitun_ue1 -c 10 192.168.72.135 + + + + Custom_Command + Ping ext-dn from UE PDU session 2 interface + localhost + docker exec rfsim5g-oai-nr-ue ping -I oaitun_ue1p2 -c 10 192.168.72.135 + + + + Iperf + Iperf (UL/1Mbps/UDP)(6 sec) QFI 1 (default) flow on PDU session 1 (port 5201) + -u -b 1M -t 6 -B 12.1.1.2 -p 5201 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 20 + 10 + + + + Iperf + Iperf (UL/1Mbps/UDP)(6 sec) QFI 2 flow on PDU session 1 (port 52080) + -u -b 1M -t 6 -B 12.1.1.2 -p 52080 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 20 + 10 + + + + Iperf + Iperf (UL/1Mbps/UDP)(6 sec) QFI 3 flow on PDU session 1 (port 52081) + -u -b 1M -t 6 -B 12.1.1.2 -p 52081 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 20 + 10 + + + + Iperf + Iperf (UL/1Mbps/UDP)(6 sec) default flow on PDU session 2 (port 5201) + -u -b 1M -t 6 -B 12.1.2.2 -p 5201 rfsim5g_ue_pdu_2 localhost rfsim5g_ext_dn localhost - -c 20 -i 0.25 - 5 + 20 + 10 + + + + Iperf + Iperf (DL/1Mbps/UDP)(6 sec) QFI 1 (default) flow on PDU session 1 (port 5201) + -u -b 1M -t 6 -R -B 12.1.1.2 -p 5201 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 20 + 10 + + + + Iperf + Iperf (DL/1Mbps/UDP)(6 sec) QFI 2 flow on PDU session 1 (port 52080) + -u -b 1M -t 6 -R -B 12.1.1.2 -p 52080 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 20 + 10 + + + + Iperf + Iperf (DL/1Mbps/UDP)(6 sec) QFI 3 flow on PDU session 1 (port 52081) + -u -b 1M -t 6 -R -B 12.1.1.2 -p 52081 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 20 + 10 diff --git a/ci-scripts/yaml_files/5g_rfsimulator/mini_nonrf_config.yaml b/ci-scripts/yaml_files/5g_rfsimulator/mini_nonrf_config.yaml index e717596481..318148d6ac 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/mini_nonrf_config.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator/mini_nonrf_config.yaml @@ -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" diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml index 60ca044f94..61f7de9fbc 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml @@ -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: diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/pcc_rules/pcc_rules.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/pcc_rules/pcc_rules.yaml new file mode 100644 index 0000000000..3a62c0ba7a --- /dev/null +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/pcc_rules/pcc_rules.yaml @@ -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 diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/policy_decisions/policy_decision.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/policy_decisions/policy_decision.yaml new file mode 100644 index 0000000000..a3ba9a7e91 --- /dev/null +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/policy_decisions/policy_decision.yaml @@ -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 diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/qos_data/qos_data.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/qos_data/qos_data.yaml new file mode 100644 index 0000000000..cd516ba12b --- /dev/null +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/policies/qos_data/qos_data.yaml @@ -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