Merge remote-tracking branch 'origin/n2-handover' into integration_2025_w38 (!3334)

N2 handover

This MR introduces full support for N2 handover, covering handover
decision, preparation, execution, and notification between source and
target NG-RAN nodes.

Key features:

1. Handover Decision & Preparation:
  - Implement N2 handover decision based on UE measurement reports.
  - Generate the Handover Preparation Information message, including
    RRCReconfiguration.
  - Send Handover Required message from the source NG-RAN to the AMF.
2. Handover Execution:
  - Decode and handle the Handover Request on the target NG-RAN.
  - Process the Handover Command on the source gNB, triggering
    RRCReconfiguration.
  - Initiate Handover Notify after successful completion.
3 NGAP & RRC Enhancements:
  - Implement NGAP encoding/decoding functions for Handover Required,
    Handover Request, Handover Command, Handover Failure, and Handover
    Notify (3GPP TS 38.413).
  - Adds NG-RAN Status Transfer support (UL/DL) for PDCP COUNT
    preservation during mobility:
    - support for NGAP Uplink and Downlink RAN Status Transfer messages
      (TS 38.413 §9.2.3.13-14) to enable proper PDCP COUNT value
      transfer during handover or recovery: NGAP encoding/decoding for
      RAN Status Transfer, PDCP helpers to extract and apply COUNT (HFN
      + SN) per DRB. This aligns with TS 38.300 §9.2.3.2.1 and TS 23.502
      §4.9.1.3.3, ensuring PDCP status preservation in AM.
4. Testing:
  - Introduce a Telnet command to manually trigger N2 handover using UE
    ID and neighbor PCI.
  - Add configuration file for testing N2 handover with a target gNB.
This commit is contained in:
Robert Schmidt
2025-09-18 11:57:44 +02:00
52 changed files with 4922 additions and 286 deletions
+1
View File
@@ -363,6 +363,7 @@ add_library(ngap
${NGAP_DIR}/ngap_gNB.c
${NGAP_DIR}/ngap_common.c
${NGAP_DIR}/ngap_gNB_context_management_procedures.c
${NGAP_DIR}/ngap_gNB_mobility_management.c
${NGAP_DIR}/ngap_gNB_decoder.c
${NGAP_DIR}/ngap_gNB_encoder.c
${NGAP_DIR}/ngap_gNB_handlers.c
@@ -15,7 +15,7 @@ gNBs =
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
@include "neighbour-config.conf"
nr_cellid = 12345678L;
@@ -0,0 +1,82 @@
Active_gNBs = ( "cu-cp-0");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
# cell_type = "CELL_MACRO_GNB";
gNB_name = "cu-cp-0";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
@include "neighbour-config.conf"
nr_cellid = 12345678L;
tr_s_preference = "f1";
local_s_address = "192.168.71.150";
remote_s_address = "192.168.71.171";
local_s_portd = 2153;
remote_s_portd = 2153;
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.71.132"; });
E1_INTERFACE =
(
{
type = "cp";
ipv4_cucp = "192.168.71.150";
port_cucp = 38462;
ipv4_cuup = "0.0.0.0"; # multiple CU-UPs
port_cuup = 38462;
}
)
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.150";
};
}
);
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config : {
global_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
f1ap_log_level = "info";
ngap_log_level = "info";
};
+47 -48
View File
@@ -1,52 +1,51 @@
neighbour_list = (
{
nr_cellid = 1;
neighbour_cell_configuration = (
{
gNB_ID = 0xb00;
nr_cellid = 2;
physical_cellId = 0x002;
absoluteFrequencySSB = 641280 ;
subcarrierSpacing = 1; #30 KHz
plmn = { mcc = 216; mnc = 03; mnc_length = 2};
tracking_area_code = 2;
},
{
gNB_ID = 0xa00;
nr_cellid = 3;
physical_cellId = 0x003;
absoluteFrequencySSB = 641280 ;
subcarrierSpacing = 1; #30 KHz
plmn = { mcc = 999; mnc = 99; mnc_length = 2};
tracking_area_code = 3;
},
{
gNB_ID = 0xc00;
nr_cellid = 4;
physical_cellId = 0x004;
absoluteFrequencySSB = 641280 ;
subcarrierSpacing = 1; #30 KHz
plmn = { mcc = 999; mnc = 99; mnc_length = 2};
tracking_area_code = 4;
}
)
},
{
nr_cellid = 2;
neighbour_cell_configuration = (
{
gNB_ID = 0xe00;
nr_cellid = 1;
physical_cellId = 0x000;
absoluteFrequencySSB = 641280 ;
subcarrierSpacing = 1; #30 KHz
plmn = { mcc = 001; mnc = 01; mnc_length = 2};
tracking_area_code = 1;
}
)
}
);
############################################################
# gNB-to-gNB neighbour list + measurement configuration #
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
############################################################
neighbour_list = (
##########################################################
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 1) #
##########################################################
{
nr_cellid = 0; # Serving cell of gNB 0xe00
neighbour_cell_configuration = (
{
gNB_ID = 0xb00;
nr_cellid = 1; # Cell served by gNB 0xb00
physical_cellId = 1;
absoluteFrequencySSB= 621312;
subcarrierSpacing = 1; # 30 kHz
band = 78;
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
tracking_area_code = 1;
}
);
},
##########################################################
# Entry USED BY gNB_ID = 0xb00 (nr_cellid = 2) #
##########################################################
{
nr_cellid = 1; # Serving cell of gNB 0xb00
neighbour_cell_configuration = (
{
gNB_ID = 0xe00;
nr_cellid = 0; # Cell served by gNB 0xe00
physical_cellId = 0;
absoluteFrequencySSB= 641280;
subcarrierSpacing = 1; # 30 kHz
band = 78;
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
tracking_area_code = 1;
}
);
}
);
############################################################
# Common NR measurement-event configuration #
############################################################
nr_measurement_configuration = {
Periodical = {
@@ -0,0 +1,207 @@
<!--
Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The OpenAirInterface Software Alliance licenses this file to You under
the OAI Public License, Version 1.1 (the "License"); you may not use this file
except in compliance with the License.
You may obtain a copy of the License at
http://www.openairinterface.org/?page_id=698
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
For more information about the OpenAirInterface (OAI) Software Alliance:
contact@openairinterface.org
-->
<testCaseList>
<htmlTabRef>rfsim-5gnr-f1-n2-ho</htmlTabRef>
<htmlTabName>F1 SA TDD N2 Handover</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<TestCaseRequestedList>
111111
800813
000001
000002
000003
020001
020002
030001
030002
000004
040001
050001
020001
020002
050002
030001
030002
050003
020001
020002
050004
050001
020001
020002
050002
100001
222222
</TestCaseRequestedList>
<TestCaseExclusionList></TestCaseExclusionList>
<testCase id="111111">
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-cuup-asan oai-nr-ue-asan</images>
</testCase>
<testCase id="800813">
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="000001">
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CoreNetwork</desc>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="000002">
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CU-CP/CU-UP/DU 0 + UE RFsim SA</desc>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
<services>oai-cucp-0 oai-cuup-0 oai-du-0 oai-nr-ue</services>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="000003">
<class>Attach_UE</class>
<desc>Attach OAI UE (Wait for IP)</desc>
<id>rfsim5g_ue</id>
<nodes>acamas</nodes>
</testCase>
<testCase id="000004">
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CU-CP/CU-UP/DU 1 RFsim SA</desc>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
<services>oai-cucp-1 oai-cuup-1 oai-du-1</services>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="020001">
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>
<id>rfsim5g_ext_dn</id>
<nodes>acamas</nodes>
<svr_id>rfsim5g_ue</svr_id>
<svr_node>acamas</svr_node>
<ping_args>-c 100 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase id="020002">
<class>Ping</class>
<desc>Ping ext-dn from NR-UE</desc>
<id>rfsim5g_ue</id>
<nodes>acamas</nodes>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>acamas</svr_node>
<ping_args>-c 100 -i 0.25</ping_args>
<ping_packetloss_threshold>10</ping_packetloss_threshold>
</testCase>
<testCase id="030001">
<class>Iperf</class>
<desc>Iperf (DL/10Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 3M -t 20 -R</iperf_args>
<id>rfsim5g_ue</id>
<nodes>acamas</nodes>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>acamas</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase id="030002">
<class>Iperf</class>
<desc>Iperf (UL/4Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 1M -t 20</iperf_args>
<id>rfsim5g_ue</id>
<nodes>acamas</nodes>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>acamas</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase id="040001">
<class>IdleSleep</class>
<desc>Sleep</desc>
<idle_sleep_time_in_sec>5</idle_sleep_time_in_sec>
</testCase>
<testCase id="050001">
<class>Custom_Command</class>
<desc>Trigger N2 Handover</desc>
<node>acamas</node>
<command>echo ci trigger_n2_ho 1,1 | ncat 192.168.71.150 9090</command>
<command_fail>yes</command_fail>
</testCase>
<testCase id="050002">
<class>Custom_Command</class>
<desc>UE (1) connected to DU-PCI 1?</desc>
<node>acamas</node>
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.180 9090 | grep "1234"</command>
</testCase>
<testCase id="050003">
<class>Custom_Command</class>
<desc>Trigger N2 Handover</desc>
<node>acamas</node>
<command>echo ci trigger_n2_ho 0,1 | ncat 192.168.71.180 9090</command>
<command_fail>yes</command_fail>
</testCase>
<testCase id="050004">
<class>Custom_Command</class>
<desc>UE (1) connected to DU-PCI 0?</desc>
<node>acamas</node>
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3584"</command>
</testCase>
<testCase id="100001">
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy all OAI 5G stack</desc>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_n2_ho</yaml_path>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
<d_retx_th>10,100,100,100</d_retx_th>
<u_retx_th>10,100,100,100</u_retx_th>
</testCase>
<testCase id="222222">
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-cuup-asan oai-nr-ue-asan</images>
</testCase>
</testCaseList>
@@ -110,6 +110,7 @@ services:
public_net:
ipv4_address: 192.168.71.150
volumes:
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
- ../../conf_files/gnb-cu.sa.band78.106prb.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
@@ -0,0 +1,308 @@
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:8.0
volumes:
- ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
- ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
environment:
- TZ=Europe/Paris
- MYSQL_DATABASE=oai_db
- MYSQL_USER=test
- MYSQL_PASSWORD=test
- MYSQL_ROOT_PASSWORD=linux
healthcheck:
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
interval: 10s
timeout: 5s
retries: 30
networks:
public_net:
ipv4_address: 192.168.71.131
oai-amf:
container_name: "rfsim5g-oai-amf"
image: oaisoftwarealliance/oai-amf:ci-tmp-pr-372-26c14bcd
environment:
- TZ=Europe/paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
depends_on:
- mysql
networks:
public_net:
ipv4_address: 192.168.71.132
oai-smf:
container_name: "rfsim5g-oai-smf"
image: oaisoftwarealliance/oai-smf:v2.1.9
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.133
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.1.9
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
depends_on:
- oai-smf
cap_add:
- NET_ADMIN
- SYS_ADMIN
cap_drop:
- ALL
privileged: true
networks:
public_net:
ipv4_address: 192.168.71.134
interface_name: eth0
traffic_net:
ipv4_address: 192.168.72.134
interface_name: eth1
oai-ext-dn:
privileged: true
container_name: rfsim5g-oai-ext-dn
image: oaisoftwarealliance/trf-gen-cn5g:focal
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"
depends_on:
- oai-upf
networks:
traffic_net:
ipv4_address: 192.168.72.135
healthcheck:
test: /bin/bash -c "ping -c 2 192.168.72.134"
interval: 10s
timeout: 5s
retries: 5
oai-cucp-0:
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
container_name: rfsim5g-oai-cucp-0
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --rfsim --rfsimulator.serveraddr 192.168.71.190
--telnetsrv --telnetsrv.shrmod ci
--telnetsrv.listenaddr 192.168.71.150
--gNBs.[0].nr_cellid 0
--log_config.global_log_options level,nocolor,time
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.150
volumes:
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
- ../../conf_files/gnb-cucp.sa.e1-ho-n2.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
oai-cuup-0:
image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop}
container_name: rfsim5g-oai-cuup-0
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --log_config.global_log_options level,nocolor,time
--gNBs.[0].E1_INTERFACE.[0].ipv4_cucp 192.168.71.150
--gNBs.[0].E1_INTERFACE.[0].ipv4_cuup 192.168.71.151
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NGU 192.168.71.151
--gNBs.[0].local_s_address 192.168.71.151
ASAN_OPTIONS: detect_leaks=0
depends_on:
- oai-cucp-0
networks:
public_net:
ipv4_address: 192.168.71.151
volumes:
- ../../conf_files/gnb-cuup.sa.f1.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-cuup"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
oai-du-0:
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
container_name: rfsim5g-oai-du-0
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --rfsim
--log_config.global_log_options level,nocolor,time
--gNBs.[0].nr_cellid 0
--telnetsrv --telnetsrv.listenaddr 192.168.71.171
--telnetsrv.shrmod ci
--rfsimulator.serveraddr 192.168.71.190
--gNBs.[0].servingCellConfigCommon.[0].ra_ResponseWindow 5
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
depends_on:
- oai-cucp-0
networks:
public_net:
ipv4_address: 192.168.71.171
volumes:
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
- ../../conf_files/gnb-du.sa.band78.106prb.rfsim.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
oai-nr-ue:
image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: --rfsim --rfsimulator.serveraddr server
--band 78 -C 3619200000 -r 106 --numerology 1 --ssb 516
--uicc0.imsi 208990100001100
--log_config.global_log_options level,nocolor,time
networks:
public_net:
ipv4_address: 192.168.71.190
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
interval: 10s
timeout: 5s
retries: 5
oai-cucp-1:
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
container_name: rfsim5g-oai-cucp-1
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --rfsim --rfsimulator.serveraddr 192.168.71.190
--telnetsrv --telnetsrv.shrmod ci
--telnetsrv.listenaddr 192.168.71.180
--gNBs.[0].E1_INTERFACE.[0].ipv4_cucp 192.168.71.180
--gNBs.[0].E1_INTERFACE.[0].ipv4_cuup 192.168.71.181
--gNBs.[0].local_s_address 192.168.71.180
--gNBs.[0].remote_s_address 192.168.71.181
--gNBs.[0].gNB_ID 0xb00
--gNBs.[0].nr_cellid 1
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NG_AMF 192.168.71.180
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NGU 192.168.71.180
--log_config.global_log_options level,nocolor,time
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
networks:
public_net:
ipv4_address: 192.168.71.180
volumes:
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
- ../../conf_files/gnb-cucp.sa.e1-ho-n2.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
oai-cuup-1:
image: ${REGISTRY:-oaisoftwarealliance}/${NRCUUP_IMG:-oai-nr-cuup}:${TAG:-develop}
container_name: rfsim5g-oai-cuup-1
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --log_config.global_log_options level,nocolor,time
--gNBs.[0].E1_INTERFACE.[0].ipv4_cucp 192.168.71.180
--gNBs.[0].E1_INTERFACE.[0].ipv4_cuup 192.168.71.181
--gNBs.[0].NETWORK_INTERFACES.GNB_IPV4_ADDRESS_FOR_NGU 192.168.71.181
--gNBs.[0].local_s_address 192.168.71.181
ASAN_OPTIONS: detect_leaks=0
depends_on:
- oai-cucp-0
networks:
public_net:
ipv4_address: 192.168.71.181
volumes:
- ../../conf_files/gnb-cuup.sa.f1.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-cuup"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
oai-du-1:
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
container_name: rfsim5g-oai-du-1
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --rfsim
--rfsimulator.serveraddr 192.168.71.190
--log_config.global_log_options level,nocolor,time
--gNBs.[0].gNB_DU_ID 1234
--gNBs.[0].gNB_ID 0xb00
--gNBs.[0].nr_cellid 1
--gNBs.[0].servingCellConfigCommon.[0].physCellId 1
--gNBs.[0].servingCellConfigCommon.[0].absoluteFrequencySSB 621312
--gNBs.[0].servingCellConfigCommon.[0].dl_absoluteFrequencyPointA 620040
--MACRLCs.[0].local_n_address 192.168.71.182
--MACRLCs.[0].remote_n_address 192.168.71.180
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
depends_on:
- oai-cucp-1
networks:
public_net:
ipv4_address: 192.168.71.182
volumes:
- ../../conf_files/neighbour-config.conf:/opt/oai-gnb/etc/neighbour-config.conf
- ../../conf_files/gnb-du.sa.band78.106prb.rfsim.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
driver: bridge
name: rfsim5g-oai-public-net
ipam:
config:
- subnet: 192.168.71.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-public"
traffic_net:
driver: bridge
name: rfsim5g-oai-traffic-net
ipam:
config:
- subnet: 192.168.72.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"
+49
View File
@@ -38,6 +38,7 @@
#include "openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair2/RRC/NR/rrc_gNB_mobility.h"
#define TELNETSERVERCODE
#include "telnetsrv.h"
@@ -196,6 +197,53 @@ int rrc_gNB_trigger_f1_ho(char *buf, int debug, telnet_printfunc_t prnt)
return 0;
}
extern void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t rrc_ue_id);
/** @brief Trigger N2 handover for UE
* @param buf: Neighbour PCI, SCell PCI, RRC UE ID
* @param debug: Debug flag
* @param prnt: Print function
* @return 0 on success, -1 on failure */
int rrc_gNB_trigger_n2_ho(char *buf, int debug, telnet_printfunc_t prnt)
{
if (!RC.nrrrc)
ERROR_MSG_RET("no RRC present, cannot list counts\n");
if (!buf) {
ERROR_MSG_RET("Please provide neighbour cell id and ue id\n");
} else {
// Parse neighbour cell PCI
char *token = strtok(buf, ",");
if (!token) {
ERROR_MSG_RET("Invalid input. Expected format: Neighbour PCI, ueId\n");
}
uint32_t neighbour_pci = strtol(token, NULL, 10);
// Parse ueId
token = strtok(NULL, ",");
if (!token) {
ERROR_MSG_RET("Missing UE ID\n");
}
uint32_t ueId = strtol(token, NULL, 10);
// Retrieve UE context
rrc_gNB_ue_context_t *ue_p = rrc_gNB_get_ue_context(RC.nrrrc[0], ueId);
if (!ue_p) {
ERROR_MSG_RET("UE with id %u not found\n", ueId);
}
gNB_RRC_UE_t *UE = &ue_p->ue_context;
// Trigger N2 handover
nr_HO_N2_trigger_telnet(RC.nrrrc[0], neighbour_pci, UE->rrc_ue_id);
// Print success message
prnt("RRC N2 handover triggered for UE %u with neighbour cell id %u\n",
ueId,
neighbour_pci);
}
return 0;
}
int force_ul_failure(char *buf, int debug, telnet_printfunc_t prnt)
{
if (!RC.nrmac)
@@ -245,6 +293,7 @@ static telnetshell_cmddef_t cicmds[] = {
{"trigger_f1_ho", "[rrc_ue_id(int,opt)]", rrc_gNB_trigger_f1_ho},
{"fetch_du_by_ue_id", "[rrc_ue_id(int,opt)]", fetch_du_by_ue_id},
{"get_current_bwp", "[rnti(hex,opt)]", get_current_bwp},
{"trigger_n2_ho", "[neighbour_pci(uint32_t),ueId(uint32_t)]", rrc_gNB_trigger_n2_ho},
{"", "", NULL},
};
+91 -2
View File
@@ -210,7 +210,7 @@ sequenceDiagram
end
```
## Handover
## Inter-DU Handover (F1)
The basic handover (HO) structure is as follows. In order to support various
handover "message passing implementation" (F1AP, NGAP, XnAP), RRC employs
@@ -264,6 +264,94 @@ sequenceDiagram
sdu->>cucp: F1AP UE Context Release Complete
Note over ue,tdu: UE active on target DU
```
## Inter-gNB Handover (N2)
This is an inter-NG-RAN procedure. The N2 handover specification is defined in the following documents:
* 3GPP TS 23.502, 4.9.1.3 Inter NG-RAN node N2 based handover:
- Outlines detailed handover signaling flows for N2-based handovers.
- Covers both intra-system (between 5G gNBs) and inter-system (between 5G and LTE eNBs) handovers.
* 3GPP TS 38.300, section 9 Mobility and State Transitions:
- describes mobility procedures at the NG-RAN level, depending on the RRC state.
* 3GPP TS 38.413 (NGAP), section 8.4 UE Mobility Management Procedures:
- Specifies the signaling procedures over the N2 interface.
- Includes messages like Handover Request, Handover Command, and Handover Preparation.
* 3GPP TS 38.331 (RRC): details the UE-level RRC procedures involved during handovers
### End-to-end flow
```mermaid
sequenceDiagram
participant ue as UE
participant sdu as source DU
participant scucp as source CU-CP
participant scuup as source CU-UP
participant tdu as target DU
participant tcucp as target CU-CP
participant tcuup as target CU-UP
participant amf as AMF
Note over ue,sdu: UE active on source DU
alt HO triggered through A3 event
ue->>sdu: RRC Measurement Report
sdu->>scucp: F1AP UL RRC Msg Transfer (RRC Measurement Report)
Note over scucp: Handover decision (A3 event trigger)
else Manual Trigger
Note over scucp: Handover decision (e.g., telnet)
end
Note over scucp: nr_rrc_trigger_n2_ho() ("on source CU")
scucp->>amf: HANDOVER REQUIRED
amf->>tcucp: HANDOVER REQUEST
Note over tcucp: rrc_gNB_process_Handover_Request
Note over tcucp: trigger_bearer_setup
tcucp->>tcuup: Bearer Context Setup Request
tcuup->>tcucp: Bearer Context Setup Response
Note over tcucp: rrc_gNB_process_e1_bearer_context_setup_resp
Note over tcucp: nr_rrc_trigger_n2_ho_target() ("on target CU")
Note over tcucp: nr_initiate_handover()
tcucp->>tdu: F1AP UE Context Setup Req
Note over tdu: Create UE context
tdu->>tcucp: F1AP UE Context Setup Resp (incl. CellGroupConfig)
Note over tcucp: rrc_CU_process_ue_context_setup_response() ("on target CU")
Note over tcucp: e1_send_bearer_updates()
tcucp->>tcuup: E1AP Bearer Context Modification Req
tcuup->>tcucp: E1AP Bearer Context Modification Resp
tcucp-->>tcucp: callback: ho_req_ack()
Note over tcucp: nr_rrc_n2_ho_acknowledge() ("on target CU")
tcucp->>amf: HANDOVER REQUEST ACKNOWLEDGE (data forwarding info)
amf->>scucp: HANDOVER COMMAND
scucp->>sdu: F1AP UE Context Modification Req (RRC Reconfiguration)
sdu->>ue: RRC Reconfiguration
sdu->>scucp: F1AP UE Context Modification Resp
Note over sdu: Stop scheduling UE
Note over scucp: rrc_CU_process_ue_context_modification_response()
Note over scucp: e1_send_bearer_updates()
scucp->>scuup: E1 Bearer Context Modification Req (PDCP Status Request)
scuup->>scucp: E1 Bearer Context Modification Resp (PDCP Status Info)
scucp->>amf: NG Uplink RAN Status Transfer
amf->>tcucp: NG Downlink RAN Status Transfer
tcucp->>tcuup: E1 Bearer Context Modification Req (PDCP Status Info)
tcuup->>tcucp: E1 Bearer Context Modification Resp
Note over ue: UE attachment to target DU
Note over ue,tdu: RA (Msg1 + Msg2)
ue->>tdu: RRC Reconfiguration Complete
tdu->>tcucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete)
tcucp-->>tcucp: callback: ho_success()
Note over tcucp: nr_rrc_n2_ho_complete() ("on target CU")
Note over tcucp: handle_rrcReconfigurationComplete() ("on target CU")
tcucp->>amf: HANDOVER NOTIFY
amf->>scucp: UE Context Release Command
note over scucp: ngap_gNB_handle_ue_context_release_command
note over scucp: rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND
scucp->>scuup: E1 Bearer Context Release Command
scuup->>scucp: E1 Bearer Context Release Complete
Note over scucp: rrc_gNB_generate_RRCRelease
scucp->>sdu: F1 UE Context Release Command
sdu->>scucp: F1 UE Context Release Complete
note over scucp: rrc_CU_process_ue_context_release_complete
note over scucp: rrc_remove_ue
Note over ue,tdu: UE active on target DU
```
# Structures
@@ -308,7 +396,8 @@ corresponding CU.
`nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for
handover cancel. `nr_ho_target_cu_t` contains function pointers `ho_req_ack`
for handover request acknowledge and `ho_success` for handover success. As can
for handover request acknowledge, `ho_success` for handover success,
`ho_failure` for handover failure (N2 only).
be seen in the sequence diagram above, either the "target CU" or "source CU"
needs to do an operation, and a "switch" from target to source CU is done using
these function pointers. For instance, in F1, the handover request acknowledge
+21 -1
View File
@@ -99,6 +99,26 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration.
### Neighbor-gNB configuration
TBD
#### What is a gNB neighbor?
Network continuity is a key aspect of 5G. In the 5G architecture, gNB neighbors play a central role in maintaining service continuity through mechanisms such as handover and load balancing. By definition, a gNB neighbor is another gNB that can be measured and linked by the UE. If the current serving gNB is no longer optimal, the UE may connect to a neighbor gNB.
To support this behavior, the network configuration specifies additional frequencies and cells that the UE should measure. The UE reports these measurements to the network, which then decides whether or not to initiate a handover.
Neighbor types include:
- **Intra-gNB neighbors** - cells belonging to the same gNB
- **Inter-gNB neighbors** - cells belonging to different gNBs
- **Inter-RAT neighbors** - cells belonging to another RAT (e.g., LTE)
#### Required configuration parameters
To define a neighbor cell in the configuration file, the following parameters are typically needed:
- **gNB ID** - unique identifier of the gNB
- **Cell ID** - identifier of the cell within the gNB
- **Physical Cell ID** - identifier of the cell’s synchronization signal (PCI)
- **Absolute Frequency** - frequency used by the SSB (absoluteFrequencySSB)
- **Subcarrier Spacing** - numerology (e.g., 15 kHz, 30 kHz)
- **PLMN configuration** - MCC, MNC, and MNC length
- **Tracking Area Code (TAC)** - identifier of the tracking area
Refer to the [handover tutorial](../handover-tutorial.md) for more information.
+146
View File
@@ -207,6 +207,7 @@ neighbour_list = (
physical_cellId = 1;
absoluteFrequencySSB = 643296;
subcarrierSpacing = 1; #30 KHz
band = 78;
plmn = { mcc = 001; mnc = 01; mnc_length = 2};
tracking_area_code = 1;
}
@@ -221,6 +222,7 @@ neighbour_list = (
physical_cellId = 0;
absoluteFrequencySSB = 643296;
subcarrierSpacing = 1; #30 KHz
band = 78;
plmn = { mcc = 001; mnc = 01; mnc_length = 2};
tracking_area_code = 1;
}
@@ -340,3 +342,147 @@ while true; do
sleep 15
done
```
# N2 Handover
## Run the setup
An N2 handover involves the transfer of a UE from one gNB to another via the 5G core network. Unlike F1 handover, where the CU handles the process internally between its DUs, N2 handover requires signaling through the AMF, making it a core-network-based handover.
We assume:
* Two independent gNBs connected to the same 5GC via N2 interface.
* A UE initially connected to gNB-PCI0, which will be handed over to gNB-PCI1.
* Handover is triggered by either by decision based measurement event (e.g. A3) or telnet command.
## Steps to run N2 handover with OAI UE
1. Similarly to F1 handover, UE does not support any measurement reporting and handover is triggered by
telnet command. Therefore, ensure that both gNBs and UE are built with telnet support:
```sh
./build_oai --ninja --nrUE --gNB --build-lib telnetsrv
```
Run the 5G Core Network if not already running. See [OAI CN5G tutorial](./NR_SA_Tutorial_OAI_CN5G.md).
2. Start the source gNB (gNB-PCI0) e.g.
```sh
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.pci0.rfsim.conf --telnetsrv --telnetsrv.shrmod ci --gNBs.[0].min_rxtxtime 6 --rfsim --rfsimulator.serveraddr 127.0.0.1
```
3. Start the UE e.g.
```sh
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --rfsim --uicc0.imsi 001010000000001 -O ../../../ci-scripts/conf_files/nrue.uicc.conf --rfsimulator.serveraddr server
```
Ensure the UE successfully registers with the network.
4. Start the target gNB (gNB PCI1) e.g.
```sh
sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.pci1.rfsim.conf --sa --rfsim --telnetsrv --telnetsrv.shrmod ci --gNBs.[0].min_rxtxtime 6 --rfsimulator.serveraddr 127.0.0.1
```
5. Trigger the N2 handover, e.g.
From gNB-PCI0, trigger handover on target gNB with PCI 1 for UE ID 1:
```sh
echo ci trigger_n2_ho 1,1 | nc 127.0.0.1 9090 && echo
```
where the input parameters correspond to the PCI of the neighbor call and the RRC ID of the UE.
This will initiate the N2 handover on the source gNB.
## Neighbour list and measurement configuration
Make sure the configuration file contains a neighbour list and measurement configuration, e.g. [neighbour-config-rfsim.conf](../../ci-scripts/conf_files/neighbour-config.conf). This configuration can also be present in a different file and included in the gNB configuration file with `@include "neighbour-config-rfsim.conf"`.
For each gNB there is a `neighbour_cell_configuration` linked to its serving cell ID.
The measurement configuration is based on A2 and A3 measurement events in 5G NR. These events are used by the UE to report radio conditions to the gNB. The A2 Measurement Event indicates that the serving cell’s signal quality has degraded below a defined threshold and the UE shall initiate measurement of neighboring cells. The A3 Measurement Event indicates that a neighboring cell’s signal quality is better than that of the serving cell by a certain offset and the UE shall trigger handover to a stronger neighboring cell
This is an example with comments on how to use the configuration file:
```
############################################################
# gNB-to-gNB neighbour list + measurement configuration #
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
############################################################
neighbour_list = (
##########################################################
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 12345678L) #
##########################################################
{
nr_cellid = 12345678L; # Serving cell of gNB 0xe00
neighbour_cell_configuration = (
{
gNB_ID = 0xb00;
nr_cellid = 720898; # Cell served by gNB 0xb00
physical_cellId = 1;
absoluteFrequencySSB= 621312;
subcarrierSpacing = 1; # 30 kHz
band = 78;
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
tracking_area_code = 1;
}
);
},
##########################################################
# Entry USED BY gNB_ID = 0xb00 (nr_cellid = 720898) #
##########################################################
{
nr_cellid = 720898; # Serving cell of gNB 0xb00
neighbour_cell_configuration = (
{
gNB_ID = 0xe00;
nr_cellid = 12345678L; # Cell served by gNB 0xe00
physical_cellId = 0;
absoluteFrequencySSB= 641280;
subcarrierSpacing = 1; # 30 kHz
band = 78;
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
tracking_area_code = 1;
}
);
}
);
############################################################
# Common NR measurement-event configuration #
############################################################
nr_measurement_configuration = {
Periodical = {
enable = 1;
includeBeamMeasurements = 1;
maxNrofRS_IndexesToReport = 4;
};
A2 = {
enable = 1;
threshold = 60;
timeToTrigger = 1;
};
A3 = (
{
cell_id = 720898; # neighbour of gNB 0xe00
offset = 10;
hysteresis = 0;
timeToTrigger = 1;
},
{
cell_id = 12345678; # neighbour of gNB 0xb00
offset = 5;
hysteresis = 1;
timeToTrigger = 2;
}
);
};
```
+20 -3
View File
@@ -283,6 +283,22 @@ typedef struct bearer_context_pdcp_config_s {
bool pDCP_Reestablishment;
} bearer_context_pdcp_config_t;
/* 9.3.1.35 3GPP TS 38.463 */
typedef struct {
// PDCP SN
uint32_t sn;
// HFN
uint32_t hfn;
} e1_pdcp_count_t;
/* 9.3.1.58 3GPP TS 38.463 */
typedef struct {
// UL Count Value
e1_pdcp_count_t ul_count;
// DL Count Value
e1_pdcp_count_t dl_count;
} e1_pdcp_status_info_t;
typedef struct drb_to_setup_s {
long drbId;
bearer_context_pdcp_config_t pdcp_config;
@@ -384,6 +400,8 @@ typedef struct DRB_nGRAN_to_modify_s {
bearer_context_pdcp_config_t *pdcp_config;
// PDCP SN Status Request (O)
bool pdcp_sn_status_requested;
// PDCP SN Status Information
e1_pdcp_status_info_t *pdcp_status;
// DL UP Transport Layer Information (O) (clause 9.3.1.13, 9.3.2.1)
int numDlUpParam;
up_params_t DlUpParamList[E1AP_MAX_NUM_UP_PARAM];
@@ -561,9 +579,8 @@ typedef struct DRB_nGRAN_modified_s {
// Flow Failed List (O)
int numQosFlowFailed;
qos_flow_failed_t qosFlowsFailed[E1AP_MAX_NUM_QOS_FLOWS];
// Old QoS Flow List (O)
int numOldQosFlow;
qos_flow_list_t oldQosFlows[E1AP_MAX_NUM_QOS_FLOWS];
// PDCP SN Status Information (0)
e1_pdcp_status_info_t *pdcp_status;
} DRB_nGRAN_modified_t;
typedef struct DRB_nGRAN_failed_s {
+10
View File
@@ -71,6 +71,13 @@ MESSAGE_DEF(NGAP_UE_CTXT_MODIFICATION_FAIL , MESSAGE_PRIORITY_MED, ngap_ue_ctxt_
MESSAGE_DEF(NGAP_PDUSESSION_SETUP_RESP , MESSAGE_PRIORITY_MED, ngap_pdusession_setup_resp_t , ngap_pdusession_setup_resp)
MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_RESP , MESSAGE_PRIORITY_MED, ngap_pdusession_modify_resp_t , ngap_pdusession_modify_resp)
MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_RESPONSE , MESSAGE_PRIORITY_MED, ngap_pdusession_release_resp_t , ngap_pdusession_release_resp)
MESSAGE_DEF(NGAP_HANDOVER_REQUIRED, MESSAGE_PRIORITY_MED, ngap_handover_required_t, ngap_handover_required)
MESSAGE_DEF(NGAP_HANDOVER_FAILURE, MESSAGE_PRIORITY_MED, ngap_handover_failure_t, ngap_handover_failure)
MESSAGE_DEF(NGAP_HANDOVER_REQUEST_ACKNOWLEDGE, MESSAGE_PRIORITY_MED, ngap_handover_request_ack_t, ngap_handover_request_ack)
MESSAGE_DEF(NGAP_HANDOVER_NOTIFY, MESSAGE_PRIORITY_MED, ngap_handover_notify_t, ngap_handover_notify)
MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t, ngap_handover_cancel)
MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer)
MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer)
/* NGAP -> RRC messages */
MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas )
@@ -81,6 +88,9 @@ MESSAGE_DEF(NGAP_PDUSESSION_SETUP_REQ , MESSAGE_PRIORITY_MED, ngap_pd
MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_REQ , MESSAGE_PRIORITY_MED, ngap_pdusession_modify_req_t , ngap_pdusession_modify_req )
MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_COMMAND , MESSAGE_PRIORITY_MED, ngap_pdusession_release_command_t , ngap_pdusession_release_command)
MESSAGE_DEF(NGAP_UE_CONTEXT_RELEASE_COMMAND, MESSAGE_PRIORITY_MED, ngap_ue_release_command_t , ngap_ue_release_command)
MESSAGE_DEF(NGAP_HANDOVER_REQUEST, MESSAGE_PRIORITY_MED, ngap_handover_request_t, ngap_handover_request)
MESSAGE_DEF(NGAP_HANDOVER_COMMAND, MESSAGE_PRIORITY_MED, ngap_handover_command_t, ngap_handover_command)
MESSAGE_DEF(NGAP_HANDOVER_CANCEL_ACK, MESSAGE_PRIORITY_MED, ngap_handover_cancel_ack_t, ngap_handover_cancel_ack)
/* NGAP <-> RRC messages (can be initiated either by MME or gNB) */
MESSAGE_DEF(NGAP_UE_CONTEXT_RELEASE_REQ , MESSAGE_PRIORITY_MED, ngap_ue_release_req_t , ngap_ue_release_req)
+301 -9
View File
@@ -65,11 +65,22 @@
#define NGAP_PDUSESSION_SETUP_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_setup_req
#define NGAP_PDUSESSION_MODIFY_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_modify_req
#define NGAP_PAGING_IND(mSGpTR) (mSGpTR)->ittiMsg.ngap_paging_ind
#define NGAP_HANDOVER_REQUIRED(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_required
#define NGAP_HANDOVER_FAILURE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_failure
#define NGAP_HANDOVER_REQUEST(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request
#define NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request_ack
#define NGAP_HANDOVER_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_command
#define NGAP_HANDOVER_NOTIFY(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_notify
#define NGAP_HANDOVER_CANCEL(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel
#define NGAP_HANDOVER_CANCEL_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel_ack
#define NGAP_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req
#define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command
#define NGAP_PDUSESSION_RELEASE_RESPONSE(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_resp
#define NGAP_UL_RAN_STATUS_TRANSFER(mSGpTR) (mSGpTR)->ittiMsg.ngap_ul_ran_status_transfer
#define NGAP_DL_RAN_STATUS_TRANSFER(mSGpTR) (mSGpTR)->ittiMsg.ngap_dl_ran_status_transfer
//-------------------------------------------------------------------------------------------//
/* Length of the transport layer address string
@@ -227,6 +238,15 @@ typedef struct ngap_mobility_restriction_s{
plmn_id_t serving_plmn;
}ngap_mobility_restriction_t;
/* PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
typedef struct {
uint8_t nb_qos;
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
pdu_session_type_t pdu_session_type;
// UPF endpoint of the NG-U (N3) transport bearer
gtpu_tunnel_t n3_incoming;
} pdusession_transfer_t;
typedef enum pdusession_qosflow_mapping_ind_e{
QOSFLOW_MAPPING_INDICATION_UL = 0,
QOSFLOW_MAPPING_INDICATION_DL = 1,
@@ -463,6 +483,244 @@ typedef struct ngap_uplink_nas_s {
byte_array_t nas_pdu;
} ngap_uplink_nas_t;
typedef struct target_cell_id_s {
plmn_id_t plmn_identity;
uint32_t nrCellIdentity;
} cell_id_t;
typedef struct target_ran_node_id_s {
uint32_t targetgNBId;
plmn_id_t plmn_identity;
uint32_t tac;
} target_ran_node_id_t;
/* 3GPP TS 38.413 9.3.1.29 */
typedef struct {
// QoS Flow Identifier
uint8_t qfi;
} qosflow_info_t;
/* 3GPP TS 38.413 9.3.1.29 */
typedef struct {
// PDU Session ID
uint8_t pdusession_id;
// QoS Flow Information List
uint8_t nb_of_qos_flow;
qosflow_info_t qos_flow_info[QOSFLOW_MAX_VALUE];
} pdusession_resource_info_t;
/* 3GPP TS 38.413 9.3.1.97 */
typedef struct {
// Global Cell ID
cell_id_t id;
// Cell type
uint8_t type;
// Time UE Stayed in Cell
long time_in_cell;
// Cause
ngap_cause_t *cause;
} last_visited_ngran_cell_info_t;
/* 3GPP TS 38.413 9.3.1.29 */
typedef struct {
// RRC Container: HandoverPreparationInformation message
byte_array_t handoverInfo;
// Target Cell ID
cell_id_t targetCellId;
// PDU Session Resource Information List
uint16_t nb_pdu_session_resource;
pdusession_resource_info_t pdu_session_resource[NGAP_MAX_PDU_SESSION];
// UE History Information
last_visited_ngran_cell_info_t ue_history_info;
} source_to_target_transparent_container_t;
typedef enum {
HANDOVER_TYPE_INTRA5GS, // Intra5GS: NG-RAN node to NG-RAN node
HANDOVER_TYPE_5GSTOEPS, // 5GStoEPS: NG-RAN node to eNB
HANDOVER_TYPE_EPSTO5GS, // EPSto5GS: eNB to NG-RAN node
HANDOVER_TYPE_5GSTOUTRA, // 5GStoUTRA: NG-RAN node to UTRA
} ho_type_t;
/* 3GPP TS 38.413 9.2.3.1 */
typedef struct {
// PDU Session ID
uint8_t pdusession_id;
// Handover Required Transfer
byte_array_t ho_required_transfer;
} pdusession_resource_t;
/* 3GPP TS 38.413 9.2.3.1 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// Handover Type
ho_type_t handoverType;
// Cause
ngap_cause_t cause;
// Target ID
target_ran_node_id_t target_gnb_id;
// PDU Session Resource List
uint8_t nb_of_pdusessions;
pdusession_resource_t pdusessions[NGAP_MAX_PDU_SESSION];
// Source to Target Transparent Container
source_to_target_transparent_container_t *source2target;
} ngap_handover_required_t;
/* 3GPP TS 38.413 9.2.3.6 */
typedef struct {
// AMF UE NGAP ID (M)
uint64_t amf_ue_ngap_id;
// Cause (M)
ngap_cause_t cause;
} ngap_handover_failure_t;
typedef struct {
// Next-Hop NH
uint8_t next_hop[SECURITY_KEY_LENGTH];
// Next Hop Chaining Count
uint8_t next_hop_chain_count;
} ngap_security_context_t;
/* Handover Request (3GPP TS 38.413 9.2.3.4)
PDU Session Resource Setup Item */
typedef struct {
// PDU Session ID
uint8_t pdusession_id;
pdu_session_type_t pdu_session_type;
// S-NSSAI
nssai_t nssai;
// Handover Required Transfer
pdusession_transfer_t pdusessionTransfer;
} ho_request_pdusession_t;
/* 3GPP TS 38.413 9.2.3.4 */
typedef struct {
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// Handover Type
ho_type_t ho_type;
// Cause
ngap_cause_t cause;
// UE Aggregate Maximum Bit Rate
ngap_ambr_t ue_ambr;
// UE Security Capabilities
ngap_security_capabilities_t security_capabilities;
// Security Context
ngap_security_context_t security_context;
// PDU Session Resource Setup List
uint8_t nb_of_pdusessions;
ho_request_pdusession_t pduSessionResourceSetupList[NGAP_MAX_PDU_SESSION];
// Allowed NSSAI
uint8_t nb_allowed_nssais;
nssai_t allowed_nssai[8];
// Source to Target Transparent Container contents
uint64_t nr_cell_id;
byte_array_t ue_ho_prep_info;
byte_array_t ue_cap;
// Mobility Restriction List
ngap_mobility_restriction_t *mobility_restriction;
// GUAMI
nr_guami_t guami;
} ngap_handover_request_t;
/* 9.3.4.11 3GPP TS 38.413 */
typedef struct {
// QoS Flow Setup Response List
uint8_t nb_of_qos_flow;
pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE];
// DL NG-U UP TNL Information
uint32_t gtp_teid;
transport_layer_addr_t gNB_addr;
} ho_request_ack_transfer_t;
/* 9.2.3.5 3GPP TS 38.413 */
typedef struct {
// PDU Session ID (M)
uint8_t pdu_session_id;
// Handover Request Acknowledge Transfer (M)
ho_request_ack_transfer_t ack_transfer;
} pdu_session_resource_admitted_t;
typedef struct {
// AMF UE NGAP ID
uint32_t gNB_ue_ngap_id;
// RAN UE NGAP ID
uint64_t amf_ue_ngap_id;
// PDU Session Resource Admitted List
pdu_session_resource_admitted_t pdusessions[NGAP_MAX_PDU_SESSION];
uint8_t nb_of_pdusessions;
// Target to Source Transparent Container
byte_array_t target2source;
} ngap_handover_request_ack_t;
/* 9.3.4.10 3GPP TS 38.413 */
typedef struct {
// QoS Flow to be Forwarded List
uint8_t nb_of_qos_flow;
pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE];
// UL Forwarding UP TNL Information
uint32_t gtp_teid;
transport_layer_addr_t gNB_addr;
} ho_command_transfer_t;
typedef struct {
// PDU Session ID (M)
uint8_t pdusession_id;
// Handover Command Transfer (M)
ho_command_transfer_t ho_command_transfer;
} pdusession_resource_handover_t;
typedef struct {
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// Handover Type
ho_type_t handoverType;
// PDU Session Resource Handover List
uint8_t nb_of_pdusessions;
pdusession_resource_handover_t pdu_sessions[NGAP_MAX_PDU_SESSION];
// Target to Source Transparent Container
byte_array_t handoverCommand;
} ngap_handover_command_t;
/* 9.3.1.16 3GPP TS 38.413 */
typedef struct {
// NR user location information
target_ran_node_id_t target_ng_ran;
uint32_t nrCellIdentity;
} user_location_information_t;
/* 9.2.3.7 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// User Location Information
user_location_information_t user_info;
} ngap_handover_notify_t;
/* 9.2.3.11 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// Cause
ngap_cause_t cause;
} ngap_handover_cancel_t;
/* 9.2.3.12 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
} ngap_handover_cancel_ack_t;
typedef struct ngap_ue_cap_info_ind_s {
uint32_t gNB_ue_ngap_id;
byte_array_t ue_radio_cap;
@@ -533,15 +791,6 @@ typedef struct ngap_downlink_nas_s {
byte_array_t nas_pdu;
} ngap_downlink_nas_t;
/* PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
typedef struct {
uint8_t nb_qos;
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
pdu_session_type_t pdu_session_type;
// UPF endpoint of the NG-U (N3) transport bearer
gtpu_tunnel_t n3_incoming;
} pdusession_transfer_t;
/* PDU Session Resource Setup/Modify Request Item */
typedef struct {
int pdusession_id;
@@ -737,4 +986,47 @@ typedef struct ngap_pdusession_release_resp_s {
} ngap_pdusession_release_resp_t;
/** 9.2.3.14 Uplink RAN Status Transfer (3GPP TS 38.413)
* COUNT value used for both UL and DL PDCP SN + HFN (12-bit or 18-bit SN) */
// Indicates PDCP SN length
typedef enum { NGAP_SN_LENGTH_12 = 0, NGAP_SN_LENGTH_18 = 1 } ngap_sn_length_t;
typedef struct {
// PDCP Sequence Number
uint32_t pdcp_sn;
// Hyper Frame Number
uint32_t hfn;
// SN length
ngap_sn_length_t sn_len;
} ngap_drb_count_value_t;
// DRBs Subject to Status Transfer Item
typedef struct {
// DRB ID
uint8_t drb_id;
// UL COUNT value
ngap_drb_count_value_t ul_count;
// DL COUNT value
ngap_drb_count_value_t dl_count;
} ngap_drb_status_t;
// RAN Status Transfer Transparent Container (9.3.1.108)
typedef struct {
// Number of DRBs in the list
uint8_t nb_drb;
// DRB Status List
ngap_drb_status_t drb_status_list[MAX_DRBS_PER_UE];
} ngap_ran_status_container_t;
// Uplink RAN Status Transfer message (9.2.3.14)
typedef struct {
// AMF UE NGAP ID (Mandatory)
uint64_t amf_ue_ngap_id;
// RAN UE NGAP ID (Mandatory)
uint32_t gnb_ue_ngap_id;
// RAN Status Transfer Transparent Container (Mandatory)
ngap_ran_status_container_t ran_status;
} ngap_ran_status_transfer_t;
#endif /* NGAP_MESSAGES_TYPES_H_ */
@@ -360,6 +360,39 @@ static E1AP_UP_Parameters_t encode_dl_up_parameters(const int numUpParam, const
return out;
}
/** @brief PDCP SN Status Information (9.3.1.58 3GPP TS 38.463) */
static E1AP_PDCP_SN_Status_Information_t encode_pdcp_status_info(const e1_pdcp_status_info_t *in)
{
E1AP_PDCP_SN_Status_Information_t pdcp = {0};
pdcp.pdcpStatusTransfer_DL.hFN = in->dl_count.hfn;
pdcp.pdcpStatusTransfer_DL.pDCP_SN = in->dl_count.sn;
pdcp.pdcpStatusTransfer_UL.countValue.hFN = in->ul_count.hfn;
pdcp.pdcpStatusTransfer_UL.countValue.pDCP_SN = in->ul_count.sn;
return pdcp;
}
/** @brief PDCP SN Status Information (9.3.1.58 3GPP TS 38.463) */
static e1_pdcp_status_info_t decode_pdcp_status_info(const E1AP_PDCP_SN_Status_Information_t* in)
{
e1_pdcp_status_info_t out = {0};
const E1AP_PDCP_Count_t *dl = &in->pdcpStatusTransfer_DL;
const E1AP_PDCP_Count_t *ul = &in->pdcpStatusTransfer_UL.countValue;
out.dl_count.hfn = dl->hFN;
out.dl_count.sn = dl->pDCP_SN;
out.ul_count.hfn = ul->hFN;
out.ul_count.sn = ul->pDCP_SN;
return out;
}
static bool eq_pdcp_info(const e1_pdcp_status_info_t *a, const e1_pdcp_status_info_t *b)
{
_E1_EQ_CHECK_INT(a->dl_count.hfn, b->dl_count.hfn);
_E1_EQ_CHECK_INT(a->dl_count.sn, b->dl_count.sn);
_E1_EQ_CHECK_INT(a->ul_count.hfn, b->ul_count.hfn);
_E1_EQ_CHECK_INT(a->ul_count.sn, b->ul_count.sn);
return true;
}
/**
* @brief Equality check for DRB_nGRAN_to_setup_t
*/
@@ -1409,6 +1442,11 @@ static E1AP_PDU_Session_Resource_To_Modify_Item_t e1_encode_pdu_session_to_mod_i
asn1cCalloc(drb2Mod->pDCP_SN_Status_Request, pDCP_SN_Status_Request);
*pDCP_SN_Status_Request = E1AP_PDCP_SN_Status_Request_requested;
}
// PDCP Status Information (O)
if (j->pdcp_status) {
asn1cCalloc(drb2Mod->pdcp_SN_Status_Information, pdcp);
*pdcp = encode_pdcp_status_info(j->pdcp_status);
}
// DL UP TNL parameters (O)
if (j->numDlUpParam > 0) {
asn1cCalloc(drb2Mod->dL_UP_Parameters, DL_UP_Param_List);
@@ -1658,7 +1696,7 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E
drb->id = drb2Mod->dRB_ID;
// PDCP Config (O)
if (drb2Mod->pDCP_Configuration) {
drb->pdcp_config = malloc_or_fail(sizeof(*drb->pdcp_config));
drb->pdcp_config = calloc_or_fail(1, sizeof(*drb->pdcp_config));
CHECK_E1AP_DEC(e1_decode_pdcp_config(drb->pdcp_config, drb2Mod->pDCP_Configuration));
}
// SDAP Config (O)
@@ -1667,9 +1705,14 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E
CHECK_E1AP_DEC(e1_decode_sdap_config(drb->sdap_config, drb2Mod->sDAP_Configuration));
}
// PDCP SN Status Request (O)
if (drb2Mod->pDCP_SN_Status_Request) {
if (drb2Mod->pDCP_SN_Status_Request && *drb2Mod->pDCP_SN_Status_Request == E1AP_PDCP_SN_Status_Request_requested) {
drb->pdcp_sn_status_requested = true;
}
// PDCP SN Status Information (O)
if (drb2Mod->pdcp_SN_Status_Information) {
drb->pdcp_status = malloc_or_fail(sizeof(*drb->pdcp_status));
*drb->pdcp_status = decode_pdcp_status_info(drb2Mod->pdcp_SN_Status_Information);
}
// DL UP TNL parameters (O)
if (drb2Mod->dL_UP_Parameters) {
E1AP_UP_Parameters_t *dl_up_paramList = drb2Mod->dL_UP_Parameters;
@@ -1843,6 +1886,7 @@ static DRB_nGRAN_to_mod_t cp_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg)
cp = *msg;
_E1_CP_OPTIONAL_IE(&cp, msg, sdap_config);
_E1_CP_OPTIONAL_IE(&cp, msg, pdcp_config);
_E1_CP_OPTIONAL_IE(&cp, msg, pdcp_status);
return cp;
}
@@ -1904,12 +1948,15 @@ static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t
_E1_EQ_CHECK_INT(a->numDlUpParam, b->numDlUpParam);
for (int i = 0; i < a->numDlUpParam; i++) {
_E1_EQ_CHECK_INT(a->DlUpParamList[i].cell_group_id, b->DlUpParamList[i].cell_group_id);
eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info);
result &= eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info);
}
if (a->pdcp_config && b->pdcp_config)
result &= eq_pdcp_config(a->pdcp_config, b->pdcp_config);
if (a->sdap_config && b->sdap_config)
result &= eq_sdap_config(a->sdap_config, b->sdap_config);
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, pdcp_status);
if (a->pdcp_status && b->pdcp_status)
result &= eq_pdcp_info(a->pdcp_status, b->pdcp_status);
return result;
}
@@ -1984,6 +2031,7 @@ static void free_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg)
{
free(msg->pdcp_config);
free(msg->sdap_config);
free(msg->pdcp_status);
}
/* Free PDU Session to modify item */
@@ -2111,6 +2159,13 @@ struct E1AP_E1AP_PDU *encode_E1_bearer_context_mod_response(const e1ap_bearer_mo
asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail);
encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]);
}
// PDCP Status Information (O)
if (drb->pdcp_status) {
asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp);
*pdcp = encode_pdcp_status_info(drb->pdcp_status);
}
// UL UP Parameters (O)
if (drb->numUpParam)
drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters));
@@ -2310,6 +2365,11 @@ bool decode_E1_bearer_context_mod_response(e1ap_bearer_modif_resp_t *out, const
drbMod->qosFlowsFailed[i] = decode_qos_flow_failed_item(drbIE->flow_Failed_List->list.array[i]);
}
}
// PDCP Status Info (O)
if (drbIE->pDCP_SN_Status_Information) {
drbMod->pdcp_status = malloc_or_fail(sizeof(*drbMod->pdcp_status));
*drbMod->pdcp_status = decode_pdcp_status_info(drbIE->pDCP_SN_Status_Information);
}
}
}
// DRB Failed to Mod list
@@ -2347,8 +2407,12 @@ e1ap_bearer_modif_resp_t cp_bearer_context_mod_response(const e1ap_bearer_modif_
// Shallow copy
cp = *msg;
// Deep copy PDU sessions modified items
for (int i = 0; i < msg->numPDUSessionsMod; i++)
for (int i = 0; i < msg->numPDUSessionsMod; i++) {
cp.pduSessionMod[i] = cp_pdu_session_mod_item(&msg->pduSessionMod[i]);
for (int j = 0; j < msg->pduSessionMod[i].numDRBModified; j++) {
_E1_CP_OPTIONAL_IE(&cp.pduSessionMod[i].DRBnGRanModList[j], &msg->pduSessionMod[i].DRBnGRanModList[j], pdcp_status);
}
}
return cp;
}
@@ -2359,6 +2423,8 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
{
if (!a || !b) return false; // Null-check both inputs
bool result = true;
// Basic members
_E1_EQ_CHECK_INT(a->gNB_cu_cp_ue_id, b->gNB_cu_cp_ue_id);
_E1_EQ_CHECK_INT(a->gNB_cu_up_ue_id, b->gNB_cu_up_ue_id);
@@ -2375,7 +2441,7 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
_E1_EQ_CHECK_OPTIONAL_PTR(pduA, pduB, ng_DL_UP_TL_info);
if (pduA->ng_DL_UP_TL_info && pduB->ng_DL_UP_TL_info)
eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info);
result &= eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info);
// DRB Modified List
_E1_EQ_CHECK_INT(pduA->numDRBModified, pduB->numDRBModified);
@@ -2387,6 +2453,10 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
for (int j = 0; j < drbModA->numQosFlowSetup; j++) {
_E1_EQ_CHECK_LONG(drbModA->qosFlows[j].qfi, drbModB->qosFlows[j].qfi);
}
_E1_EQ_CHECK_OPTIONAL_PTR(drbModA, drbModB, pdcp_status);
if (drbModA->pdcp_status && drbModB->pdcp_status) {
result &= eq_pdcp_info(drbModA->pdcp_status, drbModA->pdcp_status);
}
}
// DRB Failed to Modify List
@@ -2420,7 +2490,7 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
}
return true;
return result;
}
/**
@@ -2432,6 +2502,9 @@ void free_e1ap_context_mod_response(const e1ap_bearer_modif_resp_t *msg)
free(msg->pduSessionMod[i].confidentialityProtectionIndication);
free(msg->pduSessionMod[i].integrityProtectionIndication);
free(msg->pduSessionMod[i].ng_DL_UP_TL_info);
for (int j = 0; j < msg->pduSessionMod[i].numDRBModified; j++) {
free(msg->pduSessionMod[i].DRBnGRanModList[j].pdcp_status);
}
}
}
+18
View File
@@ -490,6 +490,13 @@ static void test_bearer_context_modification_request(void)
.qos_params.qos_characteristics.non_dynamic.fiveqi = 9,
};
const e1_pdcp_status_info_t dummy_pdcp_status = {
.dl_count.hfn = 12,
.dl_count.sn = 34,
.ul_count.hfn = 56,
.ul_count.sn = 78,
};
DRB_nGRAN_to_mod_t drb_to_mod = {
.numDlUpParam = 1,
.DlUpParamList[0].cell_group_id = MCG,
@@ -497,10 +504,12 @@ static void test_bearer_context_modification_request(void)
.id = 1,
.pdcp_config = malloc_or_fail(sizeof(*drb_to_mod.pdcp_config)),
.pdcp_sn_status_requested = true,
.pdcp_status = malloc_or_fail(sizeof(*drb_to_mod.pdcp_status)),
.numQosFlow2Setup = 1,
.qosFlows[0] = dummy_qos_flows,
};
*drb_to_mod.pdcp_config = dummy_pdcp_config;
*drb_to_mod.pdcp_status = dummy_pdcp_status;
pdu_session_to_mod_t pdusession_mod_item = {
.sessionId = 1,
@@ -592,12 +601,21 @@ const DRB_nGRAN_failed_t dummy_drb_failed = {
*/
static void test_bearer_context_modification_response(void)
{
const e1_pdcp_status_info_t dummy_pdcp_status = {
.dl_count.hfn = 12,
.dl_count.sn = 2,
.ul_count.hfn = 12,
.ul_count.sn = 3,
};
// DRB Modified List
DRB_nGRAN_modified_t drb_mod = {
.id = 1,
.numQosFlowSetup = 1,
.qosFlows[0].qfi = 1,
.pdcp_status = malloc_or_fail(sizeof(*drb_mod.pdcp_status)),
};
*drb_mod.pdcp_status = dummy_pdcp_status;
// DRB Setup List
DRB_nGRAN_setup_t setup = {
+27 -13
View File
@@ -722,6 +722,8 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type)
verify_section_notset(cfg, GNB_CONFIG_STRING_GNB_LIST ".[0]", GNB_CONFIG_STRING_AMF_IP_ADDRESS);
verify_section_notset(cfg, NULL, CONFIG_STRING_SECURITY);
verify_section_notset(cfg, NULL, CONFIG_STRING_NR_PDCP_LIST);
verify_section_notset(cfg, NULL, GNB_CONFIG_STRING_NEIGHBOUR_LIST);
verify_section_notset(cfg, NULL, GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION);
} // else nothing to be checked
/* other possible verifications: PNF, VNF, CU-CP, CU-UP, ...? */
@@ -1858,21 +1860,21 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc
GNBNEIGHBOURCELLPARAMS_DESC,
GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST,
neighbourpath);
LOG_D(GNB_APP, "HO LOG: For the Cell: %ld Neighbour Cell ELM NUM: %d\n", cell.nr_cell_id, NeighbourCellParamList.numelt);
LOG_I(GNB_APP, "Cell %ld has %d neighbours\n", cell.nr_cell_id, NeighbourCellParamList.numelt);
if (NeighbourCellParamList.numelt < 1)
continue;
cell.neighbour_cells = malloc_or_fail(sizeof(seq_arr_t));
seq_arr_init(cell.neighbour_cells, sizeof(nr_neighbour_cell_t));
for (int l = 0; l < NeighbourCellParamList.numelt; ++l) {
nr_neighbour_cell_t neighbourCell = {0};
neighbourCell.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr);
neighbourCell.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr);
neighbourCell.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr;
neighbourCell.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr;
neighbourCell.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr;
neighbourCell.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr;
neighbourCell.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr;
nr_neighbour_cell_t n = {0};
n.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr);
n.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr);
n.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr;
n.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr;
n.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr;
n.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr;
n.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr;
char neighbour_plmn_path[CONFIG_MAXOPTLENGTH];
snprintf(neighbour_plmn_path,
@@ -1884,10 +1886,22 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc
GNB_CONFIG_STRING_NEIGHBOUR_PLMN);
GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, neighbour_plmn_path);
neighbourCell.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
neighbourCell.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr;
neighbourCell.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr;
seq_arr_push_back(cell.neighbour_cells, &neighbourCell, sizeof(neighbourCell));
n.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
n.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr;
n.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr;
seq_arr_push_back(cell.neighbour_cells, &n, sizeof(n));
LOG_I(GNB_APP,
" [%d] neighbor ID %d cellId %ld PCI %d SCS %d SSB ARFCN %u TAC %u PLMN %03u.%0*u\n",
l,
n.gNB_ID,
n.nrcell_id,
n.physicalCellId,
n.subcarrierSpacing,
n.absoluteFrequencySSB,
n.tac,
n.plmn.mcc,
n.plmn.mnc_digit_length,
n.plmn.mnc);
}
seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell));
}
@@ -3461,11 +3461,10 @@ void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot
continue;
NR_timer_t *t = &UE->UE_sched_ctrl.transm_interrupt;
interrupt_followup_action_t a = nr_timer_is_active(t) ? UE->interrupt_action : FOLLOW_INSYNC;
// start a timer to stop scheduling UE during MeasGap, or extend timer for
// duration of measGap with existing follow-up action
if (!nr_timer_is_active(t) || nr_timer_remaining_time(t) < mgc->mgl_slots) {
nr_mac_interrupt_ue_transmission(nr_mac, UE, a, mgc->mgl_slots);
nr_mac_interrupt_ue_transmission(nr_mac, UE, mgc->mgl_slots);
}
}
}
@@ -3549,7 +3548,7 @@ int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs)
return (delay_ms << scs) + sl_ahead;
}
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots)
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots)
{
DevAssert(mac != NULL);
DevAssert(UE != NULL);
@@ -3557,7 +3556,6 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interr
nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1);
nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt);
UE->interrupt_action = action;
// it might happen that timing advance command should be sent during the UE
// inactivity time. To prevent this, set a variable as if we would have just
@@ -3565,14 +3563,27 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interr
// frames, after the inactivity of the UE.
UE->UE_sched_ctrl.ta_frame = (mac->frame - 1 + 1024) % 1024;
LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots) action %d\n", UE->rnti, slots, UE->interrupt_action);
LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots)\n", UE->rnti, slots);
return 0;
}
static void nr_mac_ue_transmission_timeout(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots)
{
DevAssert(mac != NULL);
DevAssert(UE != NULL);
NR_SCHED_ENSURE_LOCKED(&mac->sched_lock);
nr_timer_setup(&UE->UE_sched_ctrl.transm_timeout, slots, 1);
nr_timer_start(&UE->UE_sched_ctrl.transm_timeout);
LOG_I(NR_MAC, "UE %04x: Timeout for UE transmission (%d slots)\n", UE->rnti, slots);
return;
}
int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info)
{
int delay = nr_mac_get_reconfig_delay_slots(UE_info->current_DL_BWP.scs);
nr_mac_interrupt_ue_transmission(mac, UE_info, FOLLOW_OUTOFSYNC, delay);
nr_mac_ue_transmission_timeout(mac, UE_info, delay);
LOG_I(NR_MAC, "gNB-DU received the TransmissionActionIndicator with Stop value for UE %04x\n", UE_info->rnti);
return 0;
}
@@ -3641,9 +3652,11 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot)
if (nr_timer_tick(&sched_ctrl->transm_interrupt)) {
/* expired */
nr_timer_stop(&sched_ctrl->transm_interrupt);
if (UE->interrupt_action == FOLLOW_OUTOFSYNC)
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
/* else: default FOLLOW_INSYNC: nothing to do (UE is now active again) */
}
if (nr_timer_tick(&sched_ctrl->transm_timeout)) {
nr_timer_stop(&sched_ctrl->transm_timeout);
LOG_W(NR_MAC, "UE %04x UL failure after transmission timeout\n", UE->rnti);
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
}
}
}
+1 -1
View File
@@ -65,7 +65,7 @@ void nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, NR_UE_info_t *UE);
bool add_new_UE_RA(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE);
int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs);
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots);
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots);
int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info);
void clear_nr_nfapi_information(gNB_MAC_INST *gNB,
+4 -3
View File
@@ -688,6 +688,10 @@ typedef struct {
/// Timer for RRC processing procedures and transmission activity
NR_timer_t transm_interrupt;
/// Timer for timeout before UE is set to UL failure (e.g.,
/// "TransmissionActionIndicator" handling
NR_timer_t transm_timeout;
/// sri, ul_ri and tpmi based on SRS
nr_srs_feedback_t srs_feedback;
@@ -762,8 +766,6 @@ typedef struct measgap_config {
int mgl_slots;
} measgap_config_t;
typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_OUTOFSYNC } interrupt_followup_action_t;
/*! \brief UE list used by gNB to order UEs/CC for scheduling*/
typedef struct {
rnti_t rnti;
@@ -780,7 +782,6 @@ typedef struct {
/// in case of reestablishment, old spCellConfig to apply after
/// reconfiguration
NR_SpCellConfig_t *reconfigSpCellConfig;
interrupt_followup_action_t interrupt_action;
NR_UE_NR_Capability_t *capability;
measgap_config_t measgap_config;
// UE selected beam index
@@ -291,6 +291,32 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
&security_parameters);
}
// PDCP Status Information requested
if (to_modif->pdcp_sn_status_requested) {
modified->pdcp_status = malloc_or_fail(sizeof(*modified->pdcp_status));
nr_pdcp_count_t dl_count = {0};
nr_pdcp_count_t ul_count = {0};
nr_pdcp_get_drb_count_values(req->gNB_cu_cp_ue_id, to_modif->id, &ul_count, &dl_count);
modified->pdcp_status->dl_count.hfn = dl_count.hfn;
modified->pdcp_status->dl_count.sn = dl_count.sn;
modified->pdcp_status->ul_count.hfn = ul_count.hfn;
modified->pdcp_status->ul_count.sn = ul_count.sn;
}
// PDCP Status received (from source CU-UP)
if (to_modif->pdcp_status) {
DevAssert(to_modif->pdcp_config);
DevAssert(to_modif->pdcp_config->pDCP_SN_Size_DL == to_modif->pdcp_config->pDCP_SN_Size_UL);
bool is_sn_len_18 = to_modif->pdcp_config->pDCP_SN_Size_DL == NR_PDCP_Config__drb__pdcp_SN_SizeDL_len18bits;
nr_pdcp_count_t dl_count = { .hfn = to_modif->pdcp_status->dl_count.hfn, .sn = to_modif->pdcp_status->dl_count.sn};
nr_pdcp_count_t ul_count = { .hfn = to_modif->pdcp_status->ul_count.hfn, .sn = to_modif->pdcp_status->ul_count.sn};
nr_pdcp_count_update(req->gNB_cu_cp_ue_id,
to_modif->id,
dl_count,
ul_count,
is_sn_len_18 ? LONG_SN_SIZE : SHORT_SN_SIZE);
}
if (f1inst < 0) // no F1-U?
continue; // nothing to do
@@ -300,6 +326,7 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
in_addr_t addr = to_modif->DlUpParamList[k].tl_info.tlAddress;
GtpuUpdateTunnelOutgoingAddressAndTeid(f1inst, req->gNB_cu_cp_ue_id, to_modif->id, addr, to_modif->DlUpParamList[k].tl_info.teId);
}
}
}
+94
View File
@@ -601,6 +601,94 @@ static void nr_pdcp_entity_get_stats(nr_pdcp_entity_t *entity,
*out = entity->stats;
}
/** @note: this function applies to the gNB only */
static nr_pdcp_count_t nr_pdcp_entity_get_count_dl(nr_pdcp_entity_t *entity)
{
nr_pdcp_count_t count = {0};
const int sn_bits = entity->sn_size;
const uint32_t sn_mask = entity->sn_max;
count.hfn = entity->rx_deliv >> sn_bits;
count.sn = entity->rx_deliv & sn_mask;
return count;
}
/** @note: this function applies to the gNB only */
static nr_pdcp_count_t nr_pdcp_entity_get_count_ul(nr_pdcp_entity_t *entity)
{
nr_pdcp_count_t count = {0};
const int sn_bits = entity->sn_size;
const uint32_t sn_mask = entity->sn_max;
count.hfn = entity->tx_next >> sn_bits;
count.sn = entity->tx_next & sn_mask;
return count;
}
/** @note: this function applies to the gNB only */
static void nr_pdcp_entity_set_count_dl(nr_pdcp_entity_t *entity, nr_pdcp_count_t pdcp_count, int sn_size)
{
// SN length in bits
const uint32_t sn_mask = (1 << sn_size) - 1;
// Compose COUNT values for tx_next (DL) and rx_next (UL)
uint32_t count = (pdcp_count.hfn << sn_size) | (pdcp_count.sn & sn_mask);
// Apply the COUNT values
entity->rx_next = count + 1;
entity->rx_deliv = count;
LOG_I(PDCP,
"DRB %d: Applied PDCP Count\n"
" DL COUNT - SN: %d HFN: %d (COUNT=%u)\n"
" SN length: %s\n"
" PDCP entity state after update:\n"
" rx_next = %u\n"
" rx_deliv = %u\n"
" sn_size = %d\n"
" sn_max = %d\n"
" window = %d\n",
entity->rb_id,
pdcp_count.sn,
pdcp_count.hfn,
count,
sn_size == LONG_SN_SIZE ? "18-bit" : "12-bit",
entity->rx_next,
entity->rx_deliv,
entity->sn_size,
entity->sn_max,
entity->window_size);
}
/** @note: this function applies to the gNB only */
static void nr_pdcp_entity_set_count_ul(nr_pdcp_entity_t *entity, nr_pdcp_count_t pdcp_count, int sn_size)
{
// SN length in bits
const uint32_t sn_mask = (1 << sn_size) - 1;
// Compose COUNT values for tx_next (DL) and rx_next (UL)
uint32_t count = (pdcp_count.hfn << sn_size) | (pdcp_count.sn & sn_mask);
// Apply the COUNT values
entity->tx_next = count;
LOG_I(PDCP,
"DRB %d: Applied PDCP Count\n"
" UL COUNT - SN: %d HFN: %d (COUNT=%u)\n"
" SN length: %s\n"
" PDCP entity state after update:\n"
" tx_next = %u\n"
" sn_size = %d\n"
" sn_max = %d\n"
" window = %d\n",
entity->rb_id,
pdcp_count.sn,
pdcp_count.hfn,
count,
sn_size == LONG_SN_SIZE ? "18-bit" : "12-bit",
entity->tx_next,
entity->sn_size,
entity->sn_max,
entity->window_size);
}
nr_pdcp_entity_t *new_nr_pdcp_entity(
nr_pdcp_entity_type_t type,
@@ -675,5 +763,11 @@ nr_pdcp_entity_t *new_nr_pdcp_entity(
nr_pdcp_entity_set_security(ret, security_parameters);
/* PDCP Count API */
ret->get_pdcp_count_dl = nr_pdcp_entity_get_count_dl;
ret->get_pdcp_count_ul = nr_pdcp_entity_get_count_ul;
ret->set_pdcp_count_dl = nr_pdcp_entity_set_count_dl;
ret->set_pdcp_count_ul = nr_pdcp_entity_set_count_ul;
return ret;
}
+12 -1
View File
@@ -55,6 +55,11 @@ typedef struct {
uint8_t ciphering_key[NR_K_KEY_SIZE];
} nr_pdcp_entity_security_keys_and_algos_t;
typedef struct {
uint32_t sn;
uint32_t hfn;
} nr_pdcp_count_t;
typedef struct {
//nr_pdcp_entity_type_t mode;
/* PDU stats */
@@ -87,7 +92,6 @@ typedef struct {
uint8_t mode; /* 0: PDCP AM, 1: PDCP UM, 2: PDCP TM */
} nr_pdcp_statistics_t;
typedef struct nr_pdcp_entity_t {
nr_pdcp_entity_type_t type;
@@ -180,6 +184,13 @@ typedef struct nr_pdcp_entity_t {
/* Keep tracks of whether the PDCP entity was suspended or not */
bool entity_suspended;
/* PDCP Count API */
nr_pdcp_count_t (*get_pdcp_count_dl)(struct nr_pdcp_entity_t *entity);
nr_pdcp_count_t (*get_pdcp_count_ul)(struct nr_pdcp_entity_t *entity);
void (*set_pdcp_count_dl)(struct nr_pdcp_entity_t *entity, nr_pdcp_count_t count, int sn_size);
void (*set_pdcp_count_ul)(struct nr_pdcp_entity_t *entity, nr_pdcp_count_t count, int sn_size);
} nr_pdcp_entity_t;
nr_pdcp_entity_t *new_nr_pdcp_entity(
+54
View File
@@ -1072,3 +1072,57 @@ int nr_pdcp_get_num_ues(ue_id_t *ue_list, int len)
return num_ues;
}
void nr_pdcp_count_update(ue_id_t ue_id,
rb_id_t drb_id,
nr_pdcp_count_t dl_count,
nr_pdcp_count_t ul_count,
int sn_size)
{
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
if (ue == NULL) {
LOG_E(PDCP, "UE %ld not found in PDCP manager\n", ue_id);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
return;
}
nr_pdcp_entity_t *entity = nr_pdcp_get_rb(ue, drb_id, false);
if (entity == NULL) {
LOG_E(PDCP, "No PDCP entity found for UE %ld DRB %ld\n", ue_id, drb_id);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
return;
}
entity->set_pdcp_count_dl(entity, dl_count, sn_size);
entity->set_pdcp_count_ul(entity, ul_count, sn_size);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
}
/** * @brief Get PDCP COUNT values (PDCP-SN and HFN) for UL and DL from the PDCP entity
* @param[in] ue_id Unique UE ID
* @param[in] rb_id Radio Bearer ID
* @param[out] ul_count UL PDCP count (SN, HFN)
* @param[out] dl_count DL PDCP count (SN, HFN)
*/
void nr_pdcp_get_drb_count_values(ue_id_t ue_id, rb_id_t rb_id, nr_pdcp_count_t *ul_count, nr_pdcp_count_t *dl_count)
{
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
nr_pdcp_entity_t *entity = nr_pdcp_get_rb(ue, rb_id, false);
if (!entity) {
LOG_E(PDCP, "No entity found (DRB %ld)\n", rb_id);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
return;
}
*dl_count = entity->get_pdcp_count_dl(entity);
*ul_count = entity->get_pdcp_count_ul(entity);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
}
@@ -125,4 +125,12 @@ int nr_pdcp_get_num_ues(ue_id_t *ue_list, int len);
bool nr_pdcp_get_statistics(ue_id_t ue_id, int srb_flag, int rb_id, nr_pdcp_statistics_t *out);
void nr_pdcp_count_update(ue_id_t ue_id,
rb_id_t drb_id,
nr_pdcp_count_t dl_count,
nr_pdcp_count_t ul_count,
int sn_size);
void nr_pdcp_get_drb_count_values(ue_id_t ue_id, rb_id_t rb_id, nr_pdcp_count_t *ul_count, nr_pdcp_count_t *dl_count);
#endif /* NR_PDCP_OAI_API_H */
+271
View File
@@ -64,6 +64,10 @@
#include "NR_LogicalChannelConfig.h"
#include "NR_PDCP-Config.h"
#include "NR_MAC-CellGroupConfig.h"
#include "NR_HandoverPreparationInformation.h"
#include "NR_HandoverCommand.h"
#include "NR_HandoverCommand-IEs.h"
#include "NR_AS-Config.h"
#include "NR_SecurityModeCommand.h"
#include "NR_CipheringAlgorithm.h"
#include "NR_RRCReconfiguration-IEs.h"
@@ -82,6 +86,8 @@
#include "NR_UE-CapabilityRequestFilterNR.h"
#include "NR_HandoverPreparationInformation.h"
#include "NR_HandoverPreparationInformation-IEs.h"
#include "NR_HandoverCommand.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "common/utils/nr/nr_common.h"
#if defined(NR_Rel16)
#include "NR_SCS-SpecificCarrier.h"
@@ -197,6 +203,48 @@ int xer_nr_sprint(char *string, size_t string_size, asn_TYPE_descriptor_t *td, v
return er.encoded;
}
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len)
{
if (!buf || len == 0) return NULL;
NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList,
(void **)&clist, buf, len, 0, 0);
if (rval.code != RC_OK) {
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode container list\n", rnti);
return NULL;
}
NR_UE_NR_Capability_t *cap = decode_nr_ue_capability(rnti, clist);
ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist);
return cap;
}
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist)
{
if (!clist) return NULL;
for (int i = 0; i < clist->list.count; i++) {
const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i];
if (c->rat_Type != NR_RAT_Type_nr)
continue;
NR_UE_NR_Capability_t *cap = NULL;
asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_NR_Capability,
(void **)&cap,
c->ue_CapabilityRAT_Container.buf,
c->ue_CapabilityRAT_Container.size,
0, 0);
if (rval.code == RC_OK)
return cap;
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode NR capability (%zu bytes)\n", rnti, rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, cap);
}
return NULL;
}
//------------------------------------------------------------------------------
int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc)
@@ -564,6 +612,43 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re
return ie;
}
static byte_array_t do_HO_RRCReconfiguration(nr_rrc_reconfig_param_t *params)
{
NR_RRCReconfiguration_IEs_t *ie = build_RRCReconfiguration_IEs(params);
byte_array_t msg = {.buf = NULL, .len = 0};
// Create the standalone RRCReconfiguration message
NR_RRCReconfiguration_t rrcReconf = {0};
rrcReconf.rrc_TransactionIdentifier = params->transaction_id;
rrcReconf.criticalExtensions.present = NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration;
rrcReconf.criticalExtensions.choice.rrcReconfiguration = ie;
// Encode the message
int val = uper_encode_to_new_buffer(&asn_DEF_NR_RRCReconfiguration, NULL, &rrcReconf, (void **)&msg.buf);
if (val <= 0) {
LOG_E(NR_RRC, "ASN1 RRCReconfiguration message encoding failed\n");
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_RRCReconfiguration, &rrcReconf);
return msg;
}
msg.len = val;
LOG_D(NR_RRC, "RRCReconfiguration: Encoded (%ld bytes)\n", msg.len);
// don't free what we did not allocate, so set fields with pointers to NULL
// if memory comes from outside
ie->measConfig = NULL;
if (ie->radioBearerConfig) {
ie->radioBearerConfig->srb3_ToRelease = NULL;
ie->radioBearerConfig->drb_ToReleaseList = NULL;
ie->radioBearerConfig->drb_ToAddModList = NULL;
ie->radioBearerConfig->srb_ToAddModList = NULL;
ie->radioBearerConfig->securityConfig = NULL;
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_RRCReconfiguration, &rrcReconf);
return msg;
}
byte_array_t do_RRCReconfiguration(const nr_rrc_reconfig_param_t *params)
{
byte_array_t msg = {.buf = NULL, .len = 0};
@@ -1158,6 +1243,192 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
return mc;
}
/** @brief Prepares removal of measurement configurations related to the source gNB.
* This function decodes the HandoverPreparationInformation received from the source gNB,
* extracts the source MeasConfig, and marks those entries (measurement objects, report
* configurations, and measurement IDs) for removal by populating the corresponding
* ToRemoveList fields in the UE's current measConfig.
* This ensures that obsolete or source-specific measurement configurations are removed
* from the UE after handover and allows the target gNB to install its own valid configuration.
* @param measConfig Target gNB's current measurement configuration to be updated.
* @param prep_info Encoded HandoverPreparationInformation received from the source gNB.*/
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_array_t prep_info)
{
if (currentMC == NULL) {
LOG_E(NR_RRC, "HO LOG: UE's Measurement Configuration is NULL!\n");
return;
}
// Decodes the HandoverPreparationInformation message
NR_HandoverPreparationInformation_t *hpi = NULL;
asn_dec_rval_t hoPrep_dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_HandoverPreparationInformation,
(void **)&hpi,
(uint8_t *)prep_info.buf,
prep_info.len);
if (hoPrep_dec_rval.code != RC_OK || hoPrep_dec_rval.consumed < 0) {
LOG_E(NR_RRC, "Handover Prep Info decode error while removing source gnb measurement configuration!\n");
return;
}
/* Decodes the RRCReconfiguration message within the HandoverPreparationInformation
and extract the measConfig provided by the source gNB */
if (!hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig) {
LOG_W(NR_RRC, "Missing sourceConfig: in source gNB rrcReconfiguration\n");
return;
}
NR_RRCReconfiguration_t *rrcReconf = NULL;
NR_AS_Config_t *sourceConfig = hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig;
asn_dec_rval_t rrcReconf_dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_RRCReconfiguration,
(void **)&rrcReconf,
(uint8_t *)sourceConfig->rrcReconfiguration.buf,
sourceConfig->rrcReconfiguration.size);
if (rrcReconf_dec_rval.code != RC_OK || rrcReconf_dec_rval.consumed < 0) {
LOG_E(NR_RRC, "Failed to decode source gNB rrcReconfiguration!\n");
return;
}
if (rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig == NULL)
return;
NR_MeasConfig_t *sourceMC = rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig;
/* Add measurement objects, report configurations, and measurement IDs
to the UE's measConfig removal lists, and update UE->measConfig */
if (sourceMC->measObjectToAddModList->list.count > 0) {
currentMC->measObjectToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measObjectToRemoveList));
for (int i = 0; i < sourceMC->measObjectToAddModList->list.count; i++) {
NR_MeasObjectId_t *measObjId = calloc_or_fail(1, sizeof(NR_MeasObjectId_t));
*measObjId = sourceMC->measObjectToAddModList->list.array[i]->measObjectId;
asn1cSeqAdd(&currentMC->measObjectToRemoveList->list, measObjId);
}
}
if (sourceMC->reportConfigToAddModList->list.count > 0) {
currentMC->reportConfigToRemoveList = calloc_or_fail(1, sizeof(*currentMC->reportConfigToRemoveList));
for (int i = 0; i < sourceMC->reportConfigToAddModList->list.count; i++) {
NR_ReportConfigId_t *reportConfigId = calloc_or_fail(1, sizeof(NR_ReportConfigId_t));
*reportConfigId = sourceMC->reportConfigToAddModList->list.array[i]->reportConfigId;
asn1cSeqAdd(&currentMC->reportConfigToRemoveList->list, reportConfigId);
}
}
if (sourceMC->measIdToAddModList->list.count > 0) {
currentMC->measIdToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measIdToRemoveList));
for (int i = 0; i < sourceMC->measIdToAddModList->list.count; i++) {
NR_MeasId_t *measId = calloc_or_fail(1, sizeof(NR_MeasId_t));
*measId = sourceMC->measIdToAddModList->list.array[i]->measId;
asn1cSeqAdd(&currentMC->measIdToRemoveList->list, measId);
}
}
}
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, const byte_array_t handoverCommand)
{
// Decode Handover Command
NR_HandoverCommand_t *hoCommand = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_HandoverCommand, (void **)&hoCommand, handoverCommand.buf, handoverCommand.len);
if (dec_rval.code != RC_OK && dec_rval.consumed == 0) {
LOG_E(NR_RRC, "Can not decode Handover Command!\n");
return -1;
}
// Prepare DL DCCH message for RRCReconfiguration
NR_DL_DCCH_Message_t dl_dcch_msg = {0};
asn_enc_rval_t enc_rval;
dl_dcch_msg.message.present = NR_DL_DCCH_MessageType_PR_c1;
asn1cCalloc(dl_dcch_msg.message.choice.c1, c1);
c1->present = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration;
asn1cCalloc(c1->choice.rrcReconfiguration, rrcReconf);
// Decode RRCReconfiguration from handoverCommandMessage
OCTET_STRING_t *ho = &hoCommand->criticalExtensions.choice.c1->choice.handoverCommand->handoverCommandMessage;
uper_decode_complete(NULL, &asn_DEF_NR_RRCReconfiguration, (void **)&rrcReconf, ho->buf, ho->size);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg);
// Encode DL DCCH message in buffer
enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, &dl_dcch_msg, ba->buf, ba->len);
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg);
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
return ((enc_rval.encoded + 7) / 8);
}
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params)
{
// Encode RRCReconfiguration to buffer
byte_array_t msg = do_HO_RRCReconfiguration(params);
// Add RRCReconfiguration to handoverCommand
NR_HandoverCommand_t HoCommand = {0};
HoCommand.criticalExtensions.present = NR_HandoverCommand__criticalExtensions_PR_c1;
asn1cCalloc(HoCommand.criticalExtensions.choice.c1, c1);
c1->present = NR_HandoverCommand__criticalExtensions__c1_PR_handoverCommand;
asn1cCalloc(HoCommand.criticalExtensions.choice.c1->choice.handoverCommand, hc);
OCTET_STRING_fromBuf(&hc->handoverCommandMessage, (const char *)msg.buf, msg.len);
free_byte_array(msg);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_HandoverCommand, (void *)&HoCommand);
// Encode handoverCommand to buffer
byte_array_t buffer = {.len = -1, .buf = NULL};
buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_HandoverCommand, NULL, (void *)&HoCommand, (void **)&buffer.buf);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_HandoverCommand, &HoCommand);
return buffer;
}
/** @brief HandoverPreparationInformation message (11.2.2 of TS 38.331)
* 1) generate RRCReconfiguration to be transferred
* 2) encodes UE Capabilities from UE Context
* 3) encodes AS context
* 4) generates HO Preparation Info */
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci)
{
// Buffer to return
byte_array_t buffer = {.buf = NULL, .len = 0};
/* Prepare handoverPreparationInformation IEs */
NR_HandoverPreparationInformation_t hoPrepInfo = {0};
hoPrepInfo.criticalExtensions.present = NR_HandoverPreparationInformation__criticalExtensions_PR_c1;
asn1cCalloc(hoPrepInfo.criticalExtensions.choice.c1, c1);
c1->present = NR_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation;
asn1cCalloc(c1->choice.handoverPreparationInformation, hpi);
/* Decode stored NR_UE_CapabilityRAT_ContainerList and add to IEs */
NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
asn_dec_rval_t dec_rval =
uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList, (void **)&clist, params->ue_cap.buf, params->ue_cap.len, 0, 0);
if (dec_rval.code != RC_OK) {
LOG_W(NR_RRC, "Failed to decode UE capability container list for HandoverPreparationInformation, ignoring capabilities\n");
return buffer;
}
hpi->ue_CapabilityRAT_List = *clist;
free(clist);
/* sourceConfig: Encode RRCReconfiguration as used in the source cell */
byte_array_t msg = do_HO_RRCReconfiguration(params);
asn1cCalloc(hpi->sourceConfig, sourceConfig);
OCTET_STRING_fromBuf(&sourceConfig->rrcReconfiguration, (const char *)msg.buf, msg.len);
free_byte_array(msg);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, (void **)&hoPrepInfo);
/* encode */
buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_HandoverPreparationInformation, NULL, (void *)&hoPrepInfo, (void *)&buffer.buf);
AssertFatal(buffer.len > 0, "ASN1 message encoding failed (%ld)!\n", buffer.len);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_HandoverPreparationInformation, &hoPrepInfo);
return buffer;
}
void free_MeasConfig(NR_MeasConfig_t *mc)
{
ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, mc);
+10
View File
@@ -52,6 +52,7 @@
#include "NR_SecurityConfig.h"
#include "NR_MeasurementReport.h"
#include "NR_MeasurementTimingConfiguration.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "ds/seq_arr.h"
#include "ds/byte_array.h"
#include "rrc_messages_types.h"
@@ -71,6 +72,7 @@ typedef struct {
NR_CellGroupConfig_t *cell_group_config;
bool masterKeyUpdate;
int nextHopChainingCount;
byte_array_t ue_cap;
} nr_rrc_reconfig_param_t;
/*
@@ -172,4 +174,12 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
void free_MeasConfig(NR_MeasConfig_t *mc);
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci);
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params);
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info);
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t handoverCommand);
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len);
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist);
#endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */
+1 -1
View File
@@ -315,7 +315,7 @@ typedef struct {
int absoluteFrequencySSB;
int subcarrierSpacing;
int band;
plmn_identity_t plmn;
plmn_id_t plmn;
uint32_t tac;
bool isIntraFrequencyNeighbour;
} nr_neighbour_cell_t;
+5
View File
@@ -125,4 +125,9 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession);
f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param);
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration);
NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish);
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish);
void activate_srb(gNB_RRC_UE_t *UE, int srb_id);
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status);
#endif
+111 -40
View File
@@ -98,6 +98,7 @@
#include "NR_DL-DCCH-Message.h"
#include "ds/byte_array.h"
#include "alg/find.h"
#include "NR_HandoverCommand.h"
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
@@ -307,7 +308,7 @@ unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP)
* for the radio bearer changes, with some expections for SRB1 (i.e. when resuming
* an RRC connection, or at the first reconfiguration after RRC connection
* reestablishment in NR, do not re-establish PDCP) */
static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish)
NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish)
{
if (!ue->Srb[SRB1].Active) {
LOG_E(NR_RRC, "Call SRB list while SRB1 doesn't exist\n");
@@ -328,7 +329,7 @@ static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablis
return list;
}
static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
NR_DRB_ToAddMod_t *DRB_config = NULL;
NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);
@@ -349,12 +350,12 @@ static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
return DRB_configList;
}
static void freeSRBlist(NR_SRB_ToAddModList_t *l)
void freeSRBlist(NR_SRB_ToAddModList_t *l)
{
ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, l);
}
static void activate_srb(gNB_RRC_UE_t *UE, int srb_id)
void activate_srb(gNB_RRC_UE_t *UE, int srb_id)
{
AssertFatal(srb_id == 1 || srb_id == 2, "handling only SRB 1 or 2\n");
if (UE->Srb[srb_id].Active == 1) {
@@ -1383,6 +1384,7 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
int servingCellRSRP = 0;
int neighbourCellRSRP = 0;
int scell_pci = -1;
int best_rsrp = -10000;
switch (event_triggered->eventId.present) {
case NR_EventTriggerConfig__eventId_PR_eventA2:
@@ -1443,14 +1445,20 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
// No F1 connection but static neighbour configuration is available
const nr_a3_event_t *a3_event_configuration = get_a3_configuration(rrc, neighbour->physicalCellId);
// Additional check - This part can be modified according to additional cell specific Handover Margin
// a3-Offset: The actual value is field value * 0.5 dB.
if (a3_event_configuration
&& ((a3_event_configuration->a3_offset + a3_event_configuration->hysteresis)
&& ((a3_event_configuration->a3_offset * 0.5 + a3_event_configuration->hysteresis)
< (neighbourCellRSRP - servingCellRSRP))) {
if (neighbourCellRSRP > best_rsrp) {
// UE can send multiple neighbour cells A3 event report in 1 Meas Report. So, we need to find the best neighbour
best_rsrp = neighbourCellRSRP;
LOG_I(NR_RRC, "HO LOG: Serving Cell RSRP: %d - Best Neighbor RSRP: %d ! Trigger N2 HO\n", servingCellRSRP, best_rsrp);
nr_rrc_trigger_n2_ho(rrc, ue, scell_pci, neighbour);
}
LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id);
}
} else if (neigh_cell && neighbour) {
/* we know the cell and are connected to the DU! */
gNB_RRC_INST *rrc = RC.nrrrc[0];
nr_rrc_du_container_t *source_du = get_du_by_cell_id(rrc, serving_cell->nr_cellid);
DevAssert(source_du);
nr_rrc_du_container_t *target_du = get_du_by_cell_id(rrc, neigh_cell->nr_cellid);
@@ -1459,7 +1467,6 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
LOG_W(NR_RRC, "UE %d: received A3 event for stronger neighbor PCI %d, but no such neighbour in configuration\n", ue->rrc_ue_id, neighbour_pci);
}
}
} break;
default:
LOG_D(NR_RRC, "NR_EventTriggerConfig__eventId_PR_NOTHING or Other event report\n");
@@ -1600,21 +1607,14 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
UE->UE_Capability_nr = 0;
}
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_UE_NR_Capability,
(void **)&UE->UE_Capability_nr,
ue_cap_container->ue_CapabilityRAT_Container.buf,
ue_cap_container->ue_CapabilityRAT_Container.size,
0,
0);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = decode_nr_ue_capability(UE->rnti, ue_CapabilityRAT_ContainerList);
if (!UE->UE_Capability_nr) {
LOG_E(NR_RRC, "UE %d: NR capability decoding failed\n", UE->rrc_ue_id);
return;
}
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "UE %d: Failed to decode nr UE capabilities (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = 0;
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
}
const NR_RedCapParameters_r17_t *redcap_p = get_redcapparam_r17(UE->UE_Capability_nr);
@@ -2076,9 +2076,28 @@ static void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_
}
}
/* \brief use list of DRBs and send the corresponding bearer update message via
* E1 to the CU of this UE. Also updates TEID info internally */
static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs)
static void fill_e1_bearer_modif_pdcp_status(gNB_RRC_UE_t *UE,
DRB_nGRAN_to_mod_t *drb_to_mod,
const int drb_id,
const bool um_on_default_drb,
const ngap_drb_status_t *drb_status)
{
DevAssert(drb_status);
drb_to_mod->id = drb_id;
drb_to_mod->pdcp_sn_status_requested = false;
// PDCP SN Status Information
drb_to_mod->pdcp_config = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_config));
set_bearer_context_pdcp_config(drb_to_mod->pdcp_config, get_drb(UE, drb_id), um_on_default_drb);
drb_to_mod->pdcp_status = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_status));
drb_to_mod->pdcp_status->dl_count.hfn = drb_status->dl_count.hfn;
drb_to_mod->pdcp_status->dl_count.sn = drb_status->dl_count.pdcp_sn;
drb_to_mod->pdcp_status->ul_count.hfn = drb_status->ul_count.hfn;
drb_to_mod->pdcp_status->ul_count.sn = drb_status->ul_count.pdcp_sn;
}
/** @brief Fill and send Bearer Context Modification Request with:
* (1) from F1 UE Context DRB to setup list, or (2) with PDCP Status Request */
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status)
{
// Quit bearer updates if no CU-UP is associated
if (!is_cuup_associated(rrc)) {
@@ -2091,20 +2110,37 @@ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f
.gNB_cu_up_ue_id = UE->rrc_ue_id,
};
for (int i = 0; i < n; i++) {
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
rrc_pdu_session_param_t *pdu_ue = find_pduSession_from_drbId(UE, drb_f1->id);
if (pdu_ue == NULL) {
LOG_E(RRC, "UE %d: UE Context Modif Response: no PDU session for DRB ID %d\n", UE->rrc_ue_id, drb_f1->id);
continue;
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
int drb_id = i + 1;
DRB_nGRAN_to_mod_t drb_to_mod = {0};
if (n > 0) {
/* DRBs to Setup in F1 UE Context Modifcation Response */
if (i == n)
break;
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
drb_id = drb_f1->id;
fill_e1_bearer_modif(&drb_to_mod, drb_f1);
} else if (is_inter_cu_ho) {
/* On-going handover: send PDCP Status Request */
if (UE->established_drbs[i].status == DRB_INACTIVE)
continue;
drb_to_mod.id = drb_id;
// PDCP SN Status Request
drb_to_mod.pdcp_sn_status_requested = true;
LOG_I(NR_RRC, "PDCP Status requested (drb_id=%d)\n", drb_id);
} else if (drb_status) {
/* On-going handover: send PDCP Status */
fill_e1_bearer_modif_pdcp_status(UE, &drb_to_mod, drb_id, rrc->configuration.um_on_default_drb, drb_status);
}
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id);
DevAssert(pdu_e1 != NULL);
pdu_e1->sessionId = pdu_ue->param.pdusession_id;
DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
/* Fill E1 bearer context modification */
fill_e1_bearer_modif(drb_e1, drb_f1);
pdu_e1->numDRB2Modify += 1;
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(UE, drb_id);
if (!pdu)
continue;
pdu_session_to_mod_t *to_mod = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
DevAssert(to_mod);
to_mod->sessionId = pdu->param.pdusession_id;
// Fill E1 DRB to Modify item
to_mod->DRBnGRanModList[to_mod->numDRB2Modify++] = drb_to_mod;
}
DevAssert(req.numPDUSessionsMod > 0);
DevAssert(req.numPDUSessions == 0);
@@ -2169,7 +2205,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment");
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
if (num_drb == 0)
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
else
cuup_notify_reestablishment(rrc, UE);
}
@@ -2300,9 +2336,10 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
if (resp->drbs_len > 0) {
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
if (resp->drbs_len > 0 || is_inter_cu_ho) {
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
}
if (resp->du_to_cu_rrc_info) {
@@ -2550,6 +2587,13 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
}
}
// If HO Preparation Info is stored, N2 handover is ongoing
if (UE->ho_context) {
LOG_I(NR_RRC, "Received Bearer Context Setup Response for UE %d with valid HO Context\n", UE->rrc_ue_id);
UE->ho_context->target->ho_trigger(rrc, UE);
return;
}
if (!UE->f1_ue_context_active)
rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, resp);
else
@@ -2578,10 +2622,23 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t
return;
}
// there is not really anything to do here as of now
int n_drb_mod = 0;
e1_pdcp_status_info_t pdcp_status[MAX_DRBS_PER_UE] = {0};
for (int i = 0; i < resp->numPDUSessionsMod; ++i) {
const pdu_session_modif_t *pdu = &resp->pduSessionMod[i];
LOG_I(RRC, "UE %d: PDU session ID %ld modified %d bearers\n", resp->gNB_cu_cp_ue_id, pdu->id, pdu->numDRBModified);
for (int j = 0; j < pdu->numDRBModified; j++) {
// Trigger UL RAN Status Transfer
if (pdu->DRBnGRanModList[j].pdcp_status) {
pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status;
}
}
}
if (n_drb_mod) {
LOG_I(NR_RRC, "UE %d: received PDU Status Info - send UL RAN Status Transfer\n", resp->gNB_cu_cp_ue_id);
gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
if (ue->ho_context && ue->ho_context->source)
ue->ho_context->source->ho_status_transfer(rrc, ue, n_drb_mod, pdcp_status);
}
}
@@ -2819,6 +2876,10 @@ void *rrc_gnb_task(void *args_p) {
rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance);
break;
case NGAP_DL_RAN_STATUS_TRANSFER:
rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(msg_p, instance);
break;
/* Messages from F1AP task */
case F1AP_SETUP_REQ:
AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
@@ -2938,6 +2999,16 @@ void *rrc_gnb_task(void *args_p) {
rrc_gNB_process_PAGING_IND(msg_p, instance);
break;
case NGAP_HANDOVER_REQUEST:
rrc_gNB_process_Handover_Request(RC.nrrrc[instance], instance, &NGAP_HANDOVER_REQUEST(msg_p));
rrc_gNB_free_Handover_Request(&NGAP_HANDOVER_REQUEST(msg_p)); // Free transfered NG message
break;
case NGAP_HANDOVER_COMMAND:
rrc_gNB_process_HandoverCommand(RC.nrrrc[instance], &NGAP_HANDOVER_COMMAND(msg_p));
rrc_gNB_free_Handover_Command(&NGAP_HANDOVER_COMMAND(msg_p)); // Free transfered NG message
break;
default:
LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
break;
+378 -7
View File
@@ -39,6 +39,9 @@
#include <arpa/inet.h>
#include "E1AP_ConfidentialityProtectionIndication.h"
#include "E1AP_IntegrityProtectionIndication.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "NR_HandoverCommand.h"
#include "NR_HandoverCommand-IEs.h"
#include "NGAP_CauseRadioNetwork.h"
#include "NGAP_Dynamic5QIDescriptor.h"
#include "NGAP_GTPTunnel.h"
@@ -49,6 +52,7 @@
#include "NGAP_QosFlowSetupRequestItem.h"
#include "NGAP_asn_constant.h"
#include "NGAP_ProtocolIE-Field.h"
#include "NGAP_CellSize.h"
#include "NR_UE-NR-Capability.h"
#include "NR_UERadioAccessCapabilityInformation.h"
#include "MAC/mac.h"
@@ -82,6 +86,8 @@
#include "rrc_messages_types.h"
#include "s1ap_messages_types.h"
#include "uper_encoder.h"
#include "rrc_gNB_mobility.h"
#include "rrc_gNB_du.h"
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
@@ -162,18 +168,24 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn)
return guami;
}
/** @brief Copy NGAP PDU Session Transfer item to RRC pdusession_t struct */
static void cp_pdusession_transfer_to_pdusession(pdusession_t *dst, const pdusession_transfer_t *src)
{
dst->nb_qos = src->nb_qos;
for (uint8_t i = 0; i < src->nb_qos && i < QOSFLOW_MAX_VALUE; ++i) {
dst->qos[i] = src->qos[i];
}
dst->pdu_session_type = src->pdu_session_type;
dst->n3_incoming = src->n3_incoming;
}
/** @brief Copy NGAP PDU Session Resource item to RRC pdusession_t struct */
static void cp_pdusession_resource_item_to_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src)
{
dst->pdusession_id = src->pdusession_id;
dst->nas_pdu = src->nas_pdu;
dst->nb_qos = src->pdusessionTransfer.nb_qos;
for (uint8_t i = 0; i < src->pdusessionTransfer.nb_qos && i < QOSFLOW_MAX_VALUE; ++i) {
dst->qos[i] = src->pdusessionTransfer.qos[i];
}
dst->pdu_session_type = src->pdusessionTransfer.pdu_session_type;
dst->n3_incoming = src->pdusessionTransfer.n3_incoming;
dst->nssai = src->nssai;
cp_pdusession_transfer_to_pdusession(dst, &src->pdusessionTransfer);
}
/**
@@ -1062,7 +1074,168 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(MessageDef *msg_p, instance_t in
}
}
//-----------------------------------------------------------------------------
/** @brief Sends the NG Handover Failure from the Target NG-RAN to the AMF */
void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg)
{
LOG_I(NR_RRC, "Send NG Handover Failure message (amf_ue_ngap_id %ld) with cause %d \n ", msg->amf_ue_ngap_id, msg->cause.value);
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_FAILURE);
NGAP_HANDOVER_FAILURE(msg_p) = *msg;
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
/** @brief Process NG Handover Request message (8.4.2.2 3GPP TS 38.413) */
int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, ngap_handover_request_t *msg)
{
LOG_I(NR_RRC, "Received Handover Request (on PCI=%lu) \n", msg->nr_cell_id);
struct nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, msg->nr_cell_id);
if (du == NULL) {
/* Cell Not Found! Return HO Request Failure*/
LOG_E(RRC, "Failed to process Handover Request: no DU found with PCI=%lu \n", msg->nr_cell_id);
ngap_handover_failure_t fail = {
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
.cause.value = NGAP_CAUSE_RADIO_NETWORK_RADIO_RESOURCES_NOT_AVAILABLE,
};
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
return -1;
}
// Create UE context
sctp_assoc_t curr_assoc_id = du->assoc_id;
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_create_ue_context(curr_assoc_id, UINT16_MAX, rrc, UINT64_MAX, UINT32_MAX);
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
// allocate context for target
if (UE->ho_context != NULL) {
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", UE->rrc_ue_id);
return -1;
}
UE->ho_context = alloc_ho_ctx(HO_CTX_TARGET);
UE->ho_context->target->ho_trigger = nr_rrc_trigger_n2_ho_target;
// Store IDs in UE context
UE->amf_ue_ngap_id = msg->amf_ue_ngap_id;
UE->ue_guami = msg->guami;
UE->ho_context->target->ue_ho_prep_info = copy_byte_array(msg->ue_ho_prep_info);
// store the received UE Security Capabilities in the UE context
FREE_AND_ZERO_BYTE_ARRAY(UE->ue_cap_buffer);
UE->ue_cap_buffer = copy_byte_array(msg->ue_cap);
/* store the received Security Context in the UE context
and take it into use as defined in TS 33.501 */
set_UE_security_algos(rrc, UE, &msg->security_capabilities);
UE->nh_ncc = msg->security_context.next_hop_chain_count;
memcpy(UE->nh, msg->security_context.next_hop, SECURITY_KEY_LENGTH);
// Reset KgNB
memset(UE->kgnb, 0, SECURITY_KEY_LENGTH);
// Derive KgNB*
const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
uint32_t ssb_arfcn = get_ssb_arfcn(du);
nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, UE->nh, UE->kgnb);
UE->as_security_active = true;
// Activate SRBs
activate_srb(UE, SRB1);
activate_srb(UE, SRB2);
nr_rrc_pdcp_config_security(UE, false);
// Copy PDU Session Resource Setup item to RRC struct and do PDU Session Resource Setup procedure
pdusession_t to_setup = {
.nssai = msg->pduSessionResourceSetupList->nssai,
.pdu_session_type = msg->pduSessionResourceSetupList->pdu_session_type ,
.pdusession_id = msg->pduSessionResourceSetupList->pdusession_id,
};
cp_pdusession_transfer_to_pdusession(&to_setup, &msg->pduSessionResourceSetupList->pdusessionTransfer);
if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, &to_setup, msg->ue_ambr.br_dl)) {
LOG_E(NR_RRC, "Failed to establish PDU session: handover failed\n");
ngap_handover_failure_t fail = {
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM,
};
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail);
}
return 0;
}
void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg)
{
free_byte_array(msg->ue_cap);
free_byte_array(msg->ue_ho_prep_info);
free(msg->mobility_restriction);
}
/** @brief Send NG Uplink RAN Status Transfer message (8.4.6 3GPP TS 38.413)
* Direction: source NG-RAN node -> AMF */
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status)
{
AssertFatal(UE != NULL, "UE context is NULL\n");
LOG_I(NR_RRC,
"Sending NGAP Uplink RAN Status Transfer (AMF_UE_NGAP_ID=%" PRIu64 ", GNB_UE_NGAP_ID=%u)\n",
UE->amf_ue_ngap_id,
UE->rrc_ue_id);
ngap_ran_status_transfer_t msg = {
.amf_ue_ngap_id = UE->amf_ue_ngap_id,
.gnb_ue_ngap_id = UE->rrc_ue_id,
};
// Loop through DRBs and extract COUNT values
for (int i = 0; i < n_to_mod; ++i) {
drb_t *drb = &UE->established_drbs[i];
if (!drb->status)
continue;
bool sn_length_18 = rrc->pdcp_config.drb.sn_size == 18;
ngap_drb_status_t *item = &msg.ran_status.drb_status_list[msg.ran_status.nb_drb++];
item->drb_id = drb->drb_id;
const e1_pdcp_count_t *ul_pdcp = &pdcp_status[i].ul_count;
const e1_pdcp_count_t *dl_pdcp = &pdcp_status[i].dl_count;
item->ul_count.pdcp_sn = ul_pdcp->sn;
item->ul_count.hfn = ul_pdcp->hfn;
item->ul_count.sn_len = sn_length_18 ? NGAP_SN_LENGTH_18 : NGAP_SN_LENGTH_12;
item->dl_count.pdcp_sn = dl_pdcp->sn;
item->dl_count.hfn = dl_pdcp->hfn;
item->dl_count.sn_len = sn_length_18 ? NGAP_SN_LENGTH_18 : NGAP_SN_LENGTH_12;
}
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UL_RAN_STATUS_TRANSFER);
NGAP_UL_RAN_STATUS_TRANSFER(msg_p) = msg;
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
return 0;
}
/** @brief Process NG Handover Command on Source gNB */
void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg)
{
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, msg->gNB_ue_ngap_id);
if (ue_context_p == NULL) {
LOG_W(NR_RRC, "Unknown UE context associated to gNB_ue_ngap_id (%u)\n", msg->gNB_ue_ngap_id);
return;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
uint8_t buffer[NR_RRC_BUF_SIZE];
byte_array_t ba = {.buf = buffer, .len = sizeof(buffer)};
int enc = doRRCReconfiguration_from_HandoverCommand(&ba, msg->handoverCommand);
DevAssert(enc > 0);
rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, buffer, enc);
LOG_A(NR_RRC, "Send reconfiguration (HO Command) to UE %u/RNTI %04x\n", UE->rrc_ue_id, UE->rnti);
}
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg)
{
free_byte_array(msg->handoverCommand);
}
/*
* Process the NG command NGAP_UE_CONTEXT_RELEASE_COMMAND, sent by AMF.
* The gNB should remove all pdu session, NG context, and other context of the UE.
@@ -1172,6 +1345,88 @@ void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
LOG_I(NR_RRC,"Send message to ngap: NGAP_UE_CAPABILITIES_IND\n");
}
void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t ho_command)
{
LOG_D(NR_RRC, "Sending Handover Request Acknowledge\n");
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_REQUEST_ACKNOWLEDGE);
ngap_handover_request_ack_t *msg = &NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(msg_p);
memset(msg, 0, sizeof(*msg));
// RAN UE NGAP ID
msg->gNB_ue_ngap_id = UE->rrc_ue_id;
// AMF UE NGAP ID
msg->amf_ue_ngap_id = UE->amf_ue_ngap_id;
// PDU Session Resource Admitted List
msg->nb_of_pdusessions = UE->nb_of_pdusessions;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
rrc_pdu_session_param_t *session = &UE->pduSession[i];
session->status = PDU_SESSION_STATUS_ESTABLISHED;
// PDU Session ID
msg->pdusessions[i].pdu_session_id = session->param.pdusession_id;
// Handover Request Acknowledge Transfer
ho_request_ack_transfer_t *transfer = &msg->pdusessions[i].ack_transfer;
transfer->gtp_teid = session->param.n3_outgoing.teid;
memcpy(transfer->gNB_addr.buffer, session->param.n3_outgoing.addr.buffer, session->param.n3_outgoing.addr.length);
transfer->gNB_addr.length = session->param.n3_outgoing.addr.length;
transfer->nb_of_qos_flow = session->param.nb_qos;
for (int q = 0; q < transfer->nb_of_qos_flow; q++) {
transfer->qos_setup_list[q].qfi = session->param.qos[q].qfi;
transfer->qos_setup_list[q].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
}
// Target to Source Transparent Container
msg->target2source = copy_byte_array(ho_command);
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
/** @brief Prepare NG Handover Notify message and inform NGAP */
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
LOG_I(NR_RRC, "Triggering NGAP Handover Notify\n");
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_NOTIFY);
ngap_handover_notify_t *ho_notify = &NGAP_HANDOVER_NOTIFY(msg_p);
memset(ho_notify, 0, sizeof(*ho_notify));
nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, rrc->nr_cellid);
ho_notify->gNB_ue_ngap_id = UE->rrc_ue_id;
ho_notify->amf_ue_ngap_id = UE->amf_ue_ngap_id;
ho_notify->user_info.nrCellIdentity = rrc->nr_cellid;
ho_notify->user_info.target_ng_ran.tac = *du->setup_req->cell->info.tac;
ho_notify->user_info.target_ng_ran.targetgNBId = rrc->node_id;
ho_notify->user_info.target_ng_ran.plmn_identity.mcc = du->setup_req->cell->info.plmn.mcc;
ho_notify->user_info.target_ng_ran.plmn_identity.mnc = du->setup_req->cell->info.plmn.mnc;
ho_notify->user_info.target_ng_ran.plmn_identity.mnc_digit_length = du->setup_req->cell->info.plmn.mnc_digit_length;
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
/** @brief Prepare NG Handover Cancel (source NG-RAN) and inform NGAP (N2-based HO only)
* 3GPP TS 38.413 §8.4.5 (Handover Cancellation) - source NG-RAN -> AMF
* 3GPP TS 38.413 §9.2.3.11 (HANDOVER CANCEL)
* @param rrc CU/RRC instance
* @param UE UE context
* @param cause cancellation cause */
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause)
{
DevAssert(UE != NULL);
LOG_I(NR_RRC, "Triggering NGAP Handover Cancel (gNB_ue_ngap_id=%d, amf_ue_ngap_id=%ld)\n", UE->rrc_ue_id, UE->amf_ue_ngap_id);
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_CANCEL);
ngap_handover_cancel_t *ho_cancel = &NGAP_HANDOVER_CANCEL(msg_p);
memset(ho_cancel, 0, sizeof(*ho_cancel));
/* Mandatory IEs (38.413 §9.2.3.11) */
ho_cancel->gNB_ue_ngap_id = UE->rrc_ue_id;
ho_cancel->amf_ue_ngap_id = UE->amf_ue_ngap_id;
ho_cancel->cause = cause;
itti_send_msg_to_task(TASK_NGAP, module_id, msg_p);
}
void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid)
{
int pdu_sessions_released = 0;
@@ -1297,3 +1552,119 @@ int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance)
return 0;
}
/** @brief Callback for NGAP Handover Required message (3GPP TS 38.413 9.2.3.1)
* Direction: source gNB -> AMF */
void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *UE,
const nr_neighbour_cell_t *neighbour,
const byte_array_t hoPrepInfo)
{
LOG_I(NR_RRC, "Handover Preparation: send Handover Required (target gNB ID=%d, PCI=%d)\n", neighbour->gNB_ID, neighbour->physicalCellId);
const plmn_id_t plmn = {.mcc = neighbour->plmn.mnc,
.mnc = neighbour->plmn.mnc,
.mnc_digit_length = neighbour->plmn.mnc_digit_length};
const target_ran_node_id_t target = {
.plmn_identity = plmn,
.tac = neighbour->tac,
.targetgNBId = neighbour->gNB_ID,
};
ngap_handover_required_t msg = {
.amf_ue_ngap_id = UE->amf_ue_ngap_id,
.gNB_ue_ngap_id = UE->rrc_ue_id,
.nb_of_pdusessions = UE->nb_of_pdusessions,
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON,
.handoverType = HANDOVER_TYPE_INTRA5GS,
.target_gnb_id = target,
};
cell_id_t target_cell = {
.nrCellIdentity = neighbour->nrcell_id,
.plmn_identity = plmn,
};
// Source to Target Transparent Container (M)
msg.source2target = calloc_or_fail(1, sizeof(*msg.source2target));
// Target Cell ID (M)
msg.source2target->targetCellId = target_cell;
// RRC Container (M)
msg.source2target->handoverInfo = copy_byte_array(hoPrepInfo);
// UE History Info (M)
msg.source2target->ue_history_info.cause = malloc_or_fail(sizeof(*msg.source2target->ue_history_info.cause));
msg.source2target->ue_history_info.cause->type = NGAP_CAUSE_RADIO_NETWORK;
msg.source2target->ue_history_info.cause->value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON;
msg.source2target->ue_history_info.type = NGAP_CellSize_small;
msg.source2target->ue_history_info.time_in_cell = 500; // dummy number for now
msg.source2target->ue_history_info.id = target_cell;
/* Fill both PDU Session Resource List IE (M) in Handover Required
and PDU Session Resource Information List IE (O) in the Source NG-RAN
Node to Target NG-RAN Node Transparent Container */
for (int i = 0; i < UE->nb_of_pdusessions; ++i) {
if (UE->pduSession[i].status == PDU_SESSION_STATUS_DONE || UE->pduSession[i].status == PDU_SESSION_STATUS_ESTABLISHED) {
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, UE->pduSession[i].param.pdusession_id, false);
if (!pduSession)
continue;
// Handover Required Transfer (M)
uint8_t ho_required_transfer[128] = {0};
msg.pdusessions[i].ho_required_transfer.buf = ho_required_transfer;
msg.pdusessions[i].ho_required_transfer.len = sizeof(ho_required_transfer);
// PDU Session ID
msg.pdusessions[i].pdusession_id = pduSession->param.pdusession_id;
// PDU Session Resource Information List (O)
msg.source2target->pdu_session_resource[i].nb_of_qos_flow = pduSession->param.nb_qos;
for (int j = 0; j < pduSession->param.nb_qos; ++j) {
msg.source2target->pdu_session_resource[i].qos_flow_info[j].qfi = pduSession->param.qos[j].qfi;
}
msg.source2target->nb_pdu_session_resource++;
}
}
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_REQUIRED);
NGAP_HANDOVER_REQUIRED(msg_p) = msg;
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t instance)
{
const ngap_ran_status_transfer_t *cmd = &NGAP_DL_RAN_STATUS_TRANSFER(msg_p);
gNB_RRC_INST *rrc = RC.nrrrc[instance];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, cmd->gnb_ue_ngap_id);
if (!ue_context_p) {
LOG_E(NR_RRC, "[gNB %ld] No UE context for gNB_ue_ngap_id %u\n", instance, cmd->gnb_ue_ngap_id);
return -1;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
LOG_I(NR_RRC,
"[gNB %ld] DL RAN Status Transfer for gNB_ue_ngap_id %u AMF_UE_NGAP_ID %lu\n",
instance,
cmd->gnb_ue_ngap_id,
cmd->amf_ue_ngap_id);
for (int i = 0; i < cmd->ran_status.nb_drb; ++i) {
const ngap_drb_status_t *s = &cmd->ran_status.drb_status_list[i];
LOG_I(NR_RRC,
"DL RAN Status Transfer - DRB ID %d:\n"
" UL COUNT: PDCP SN = %u, HFN = %u (%s)\n"
" DL COUNT: PDCP SN = %u, HFN = %u (%s)\n",
s->drb_id,
s->ul_count.pdcp_sn,
s->ul_count.hfn,
s->ul_count.sn_len == NGAP_SN_LENGTH_18 ? "18-bit" : "12-bit",
s->dl_count.pdcp_sn,
s->dl_count.hfn,
s->dl_count.sn_len == NGAP_SN_LENGTH_18 ? "18-bit" : "12-bit");
// Send to PDCP layer
e1_send_bearer_updates(rrc, UE, 0, NULL, s);
}
return 0;
}
+22
View File
@@ -89,4 +89,26 @@ void nr_rrc_pdcp_config_security(gNB_RRC_UE_t *UE, bool enable_ciphering);
int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance);
void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *UE,
const nr_neighbour_cell_t *neighbourCellConfiguration,
const byte_array_t hoPrepInfo);
void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg);
int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, ngap_handover_request_t *msg);
void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg);
void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t ho_command);
void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg);
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg);
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE);
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause);
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status);
int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t instance);
#endif
+241 -30
View File
@@ -42,8 +42,7 @@
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
#endif
typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t;
static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
{
nr_handover_context_t *ho_ctx = calloc_or_fail(1, sizeof(*ho_ctx));
if (type == HO_CTX_SOURCE || type == HO_CTX_BOTH) {
@@ -58,6 +57,8 @@ static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
static void free_ho_ctx(nr_handover_context_t *ho_ctx)
{
free(ho_ctx->source);
if (ho_ctx->target)
FREE_AND_ZERO_BYTE_ARRAY(ho_ctx->target->ue_ho_prep_info);
free(ho_ctx->target);
free(ho_ctx);
}
@@ -121,18 +122,16 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
byte_array_t *ho_prep_info,
ho_req_ack_t ack,
ho_success_t success,
ho_cancel_t cancel)
ho_cancel_t cancel,
ho_failure_t failure)
{
DevAssert(rrc != NULL);
DevAssert(ue != NULL);
DevAssert(target_du != NULL);
// source_du might be NULL -> inter-CU handover
DevAssert(ho_prep_info->buf != NULL && ho_prep_info->len > 0);
DevAssert(ue->ho_context);
if (ue->ho_context != NULL) {
LOG_E(NR_RRC, "handover for UE %u ongoing, cannot trigger new\n", ue->rrc_ue_id);
return;
}
// if any reconfiguration is ongoing, abort handover request
for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
if (ue->xids[i] != RRC_ACTION_NONE) {
@@ -141,15 +140,13 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
}
}
// if we know the source DU (F1 handover), allocate the context for it,
// otherwise only for the target DU (N2, Xn)
ho_ctx_type_t ctx_type = source_du != NULL ? HO_CTX_BOTH : HO_CTX_TARGET;
nr_handover_context_t *ho_ctx = alloc_ho_ctx(ctx_type);
nr_handover_context_t *ho_ctx = ue->ho_context;
ho_ctx->target->du = target_du;
// we will know target->{du_ue_id,new_rnti} once we have UE ctxt setup
// response
ho_ctx->target->ho_req_ack = ack;
ho_ctx->target->ho_success = success;
ho_ctx->target->ho_failure = failure;
const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
if (source_du != NULL) {
@@ -164,7 +161,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup);
AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n");
}
ue->ho_context = ho_ctx;
LOG_A(NR_RRC,
"Handover triggered for UE %u/RNTI %04x towards DU %ld/assoc_id %d/PCI %d\n",
ue->rrc_ue_id,
@@ -222,27 +219,12 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
free_ue_context_setup_req(&ue_context_setup_req);
}
/*
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int nr_cgi, uint8_t *ho_prep_info, uint32_t *ho_prep_len, void *pdu_session)
{
// call from outside (via NGAP message), with the request for handover for a
// (any) UE. This handler should, in order:
//
// 1. look up the target DU =du= via get_du_by_cell_id(nr_cgi)
// (and reject the request if it can't find the DU)
// 2. create a context for a new UE =UE=, based on pdu_session information
// 3. call nr_initiate_handover(rrc, UE, NULL, du, ho_prep_info, ho_prep_len);
//
// after success of the DU, send the result via =success_ptr= to the source
// CU.
}
*/
typedef struct deliver_ue_ctxt_modification_data_t {
gNB_RRC_INST *rrc;
f1ap_ue_context_mod_req_t *modification_req;
sctp_assoc_t assoc_id;
} deliver_ue_ctxt_modification_data_t;
static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
DevAssert(deliver_pdu_data != NULL);
@@ -251,7 +233,8 @@ static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id,
data->modification_req->rrc_container = &ba;
data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req);
}
static void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
{
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
@@ -355,6 +338,13 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
uint8_t buf[NR_RRC_BUF_SIZE];
int size = do_NR_HandoverPreparationInformation(ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len, buf, sizeof buf);
// Allocate handover context (both CU and DU)
if (ue->ho_context != NULL) {
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", ue->rrc_ue_id);
return;
}
ue->ho_context = alloc_ho_ctx(HO_CTX_BOTH);
// corresponds to a "handover request", 38.300 Sec 9.3.2.3
// see also 38.413 Sec 9.3.1.29 for information on source-CU to target-CU
// information (Source NG-RAN Node to Target NG-RAN Node Transparent Container)
@@ -365,7 +355,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
ho_success_t success = nr_rrc_f1_ho_complete;
ho_cancel_t cancel = nr_rrc_cancel_f1_ho;
byte_array_t hpi = {.buf = buf, .len = size};
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel);
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel, NULL);
}
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue)
@@ -399,3 +389,224 @@ void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id)
nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du);
}
/** @brief Generate the HandoverPreparationInformation to be carried
* in the RRC Container (9.3.1.29 of 3GPP TS 38.413) of the Source
* NG-RAN Node to Target NG-RAN Node Transparent Container IE */
static byte_array_t rrc_gNB_generate_HandoverPreparationInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, int serving_pci)
{
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue, 0, false);
params.ue_cap = ue->ue_cap_buffer;
byte_array_t hoPrepInfo = get_HandoverPreparationInformation(&params, serving_pci);
free_RRCReconfiguration_params(params);
if (hoPrepInfo.len < 0) {
LOG_E(NR_RRC, "HandoverPreparationInformation generation failed for UE %d\n", ue->rrc_ue_id);
return hoPrepInfo;
}
LOG_D(NR_RRC, "HO LOG: Handover Preparation for UE %lu Encoded (%zd bytes)\n", ue->amf_ue_ngap_id, hoPrepInfo.len);
return hoPrepInfo;
}
static byte_array_t rrc_gNB_encode_HandoverCommand(gNB_RRC_UE_t *UE, gNB_RRC_INST *rrc)
{
DevAssert(UE->ho_context);
DevAssert(UE->ho_context->target);
NR_SecurityConfig_t *sec = calloc_or_fail(1, sizeof(*sec));
sec->keyToUse = calloc_or_fail(1, sizeof(*sec->keyToUse));
*sec->keyToUse = NR_SecurityConfig__keyToUse_master;
sec->securityAlgorithmConfig = calloc_or_fail(1, sizeof(*sec->securityAlgorithmConfig));
struct NR_SecurityAlgorithmConfig *alg = sec->securityAlgorithmConfig;
alg->cipheringAlgorithm = UE->ciphering_algorithm;
alg->integrityProtAlgorithm = calloc_or_fail(1, sizeof(*alg->integrityProtAlgorithm));
*alg->integrityProtAlgorithm = UE->integrity_algorithm;
// Mark source gNB's measurement configuration for removal in UE->measConfig
if (UE->ho_context->target->ue_ho_prep_info.len) {
fill_removal_lists_from_source_measConfig(UE->measConfig, UE->ho_context->target->ue_ho_prep_info);
}
/* 3GPP TS 38.331 RadioBearerConfig: re-establish PDCP whenever
the security key used for this radio bearer changes, e.g. for SRB2
when receiving reconfiguration with sync [...]. Similarly, for DRBs
PDCP re-establishment is necessary with derivation of new UP keys */
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, UE, (1 << 1) | (1 << 2), true);
UE->xids[params.transaction_id] = RRC_DEDICATED_RECONF;
params.security_config = sec;
params.masterKeyUpdate = true;
params.nextHopChainingCount = UE->nh_ncc;
byte_array_t out = get_HandoverCommandMessage(&params);
// Free remove lists in UE->measConfig
ASN_STRUCT_FREE(asn_DEF_NR_MeasIdToRemoveList, UE->measConfig->measIdToRemoveList);
ASN_STRUCT_FREE(asn_DEF_NR_ReportConfigToRemoveList, UE->measConfig->reportConfigToRemoveList);
ASN_STRUCT_FREE(asn_DEF_NR_MeasObjectToRemoveList, UE->measConfig->measObjectToRemoveList);
free_RRCReconfiguration_params(params);
return out;
}
/** @brief This callback is used by the target gNB
* to trigger the Handover Request Acknowledge towards the AMF */
static void nr_rrc_n2_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
AssertFatal(cu_exists_f1_ue_data(UE->rrc_ue_id), "No CU found for rrc_ue_id %d\n", UE->rrc_ue_id);
f1_ue_data_t previous_data = cu_get_f1_ue_data(UE->rrc_ue_id);
/* this is the callback for N2 handover after F1 UE context setup response in
* the handover case. Check that there was no associated DU, then set it */
AssertFatal(previous_data.secondary_ue == -1, "there was already a DU present\n");
previous_data.secondary_ue = UE->ho_context->target->du_ue_id;
bool success = cu_update_f1_ue_data(UE->rrc_ue_id, &previous_data);
DevAssert(success);
LOG_I(NR_RRC, "Updated CU F1AP Context for UE %d, DU Id : %u\n", UE->rrc_ue_id, UE->ho_context->target->du_ue_id);
byte_array_t hoCommand = rrc_gNB_encode_HandoverCommand(UE, rrc);
if (hoCommand.len < 0) {
LOG_E(NR_RRC, "ASN1 message encoding failed: failed to generate Handover Command Message\n");
ngap_handover_failure_t fail = {.amf_ue_ngap_id = UE->amf_ue_ngap_id,
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM};
UE->ho_context->target->ho_failure(rrc, UE->rrc_ue_id, &fail);
return;
} else {
LOG_D(NR_RRC, "HO LOG: Handover Command for UE %u Encoded (%ld bytes)\n", UE->rrc_ue_id, hoCommand.len);
}
rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(rrc, UE, hoCommand);
free_byte_array(hoCommand);
}
/** @brief This callback is used by the target gNB
* to trigger the Handover Notify towards the AMF */
static void nr_rrc_n2_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
rrc_gNB_send_NGAP_HANDOVER_NOTIFY(rrc, UE);
}
/** @brief This callback is used by the source gNB to inform the AMF
* about the cancellation of an ongoing handover */
static void nr_rrc_n2_ho_cancel(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
DevAssert(UE->ho_context);
DevAssert(UE->ho_context->source);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_CANCELLED};
rrc_gNB_send_NGAP_HANDOVER_CANCEL(rrc->module_id, UE, cause);
}
/** @brief Callback function to trigger NG Handover Failure on the target gNB, to inform the AMF
* that the preparation of resources has failed (e.g. unsatisfied criteria, gNB is already loaded).
* This message represents an Unsuccessful Outcome of the Handover Resource Allocation */
void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg)
{
LOG_I(NR_RRC, "Triggering N2 Handover Failure\n");
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, msg);
LOG_I(NR_RRC, "Send UE Context Release for gnb_ue_id %d\n", gnb_ue_id);
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, gnb_ue_id);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(rrc->module_id, ue_context_p, msg->cause);
return;
}
/** @brief Trigger N2 Handover on the target gNB:
* 1) set up callbacks
* 2) initiate N2 handover on target NG-RAN, which triggers the set up
* of HO context and UE Context Setup procedures */
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
{
ho_req_ack_t ack = nr_rrc_n2_ho_acknowledge;
ho_success_t success = nr_rrc_n2_ho_complete;
ho_failure_t failure = nr_rrc_n2_ho_failure;
const nr_rrc_du_container_t *target_du = get_du_for_ue(rrc, ue->rrc_ue_id);
nr_initiate_handover(rrc, ue, NULL, target_du, &ue->ho_context->target->ue_ho_prep_info, ack, success, NULL, failure);
FREE_AND_ZERO_BYTE_ARRAY(ue->ho_context->target->ue_ho_prep_info);
NR_UE_NR_Capability_t *ue_cap = get_ue_nr_capability(ue->rnti, ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len);
ue->UE_Capability_nr = ue_cap;
ue->UE_Capability_size = ue->ue_cap_buffer.len;
}
/** @brief Trigger N2 handover on source gNB:
* 1) Prepare RRC Container with HandoverPreparationInformation message
* 2) send NGAP Handover Required message */
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *ue,
int serving_pci,
const nr_neighbour_cell_t *neighbour_config)
{
byte_array_t hoPrepInfo = rrc_gNB_generate_HandoverPreparationInformation(rrc, ue, serving_pci);
if (hoPrepInfo.len < 0) {
free_byte_array(hoPrepInfo);
LOG_E(NR_RRC, "Failed to trigger N2 handover on source gNB for UE %x\n", ue->rrc_ue_id);
return;
}
// allocate context for source
if (ue->ho_context != NULL) {
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", ue->rrc_ue_id);
return;
}
ue->ho_context = alloc_ho_ctx(HO_CTX_SOURCE);
ue->ho_context->source->du = get_du_for_ue(rrc, ue->rrc_ue_id);
ue->ho_context->source->ho_status_transfer = rrc_gNB_send_NGAP_ul_ran_status_transfer;
ue->ho_context->source->ho_cancel = nr_rrc_n2_ho_cancel;
rrc_gNB_send_NGAP_HANDOVER_REQUIRED(rrc, ue, neighbour_config, hoPrepInfo);
free_byte_array(hoPrepInfo);
}
extern const nr_neighbour_cell_t *get_neighbour_cell_by_pci(const neighbour_cell_configuration_t *cell, int pci);
extern const neighbour_cell_configuration_t *get_neighbour_cell_config(const gNB_RRC_INST *rrc, int cell_id);
void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t rrc_ue_id)
{
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, rrc_ue_id);
if (ue_context_p == NULL) {
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: cannot find UE context\n", rrc_ue_id);
return;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
struct nr_rrc_du_container_t *du = get_du_for_ue(rrc, rrc_ue_id);
if (du == NULL) {
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: Unknown DU\n", rrc_ue_id);
return;
}
uint16_t scell_pci = du->setup_req->cell[0].info.nr_pci;
// Simulate handover on the same cell (testing purposes)
if (neighbour_pci == scell_pci) {
LOG_I(NR_RRC, "UE %d: trigger handover on the same cell PCI=%d\n", rrc_ue_id, neighbour_pci);
nr_neighbour_cell_t neighbourConfig = {
.isIntraFrequencyNeighbour = true,
.gNB_ID = du->setup_req->gNB_DU_id,
.nrcell_id = du->setup_req->cell[0].info.nr_cellid,
.physicalCellId = du->setup_req->cell[0].info.nr_pci,
.plmn = du->setup_req->cell[0].info.plmn,
.subcarrierSpacing = du->setup_req->cell[0].info.tdd.tbw.scs,
};
nr_rrc_trigger_n2_ho(rrc, UE, neighbour_pci, &neighbourConfig);
return;
}
const f1ap_served_cell_info_t *scell_du = get_cell_information_by_phycellId(scell_pci);
DevAssert(scell_du);
LOG_I(NR_RRC, "UE %d: triggered N2 HO, source PCI=%d to target PCI=%d\n", rrc_ue_id, scell_pci, neighbour_pci);
const neighbour_cell_configuration_t *cell = get_neighbour_cell_config(rrc, scell_du->nr_cellid);
const nr_neighbour_cell_t *neighbour = get_neighbour_cell_by_pci(cell, neighbour_pci);
if (neighbour == NULL) {
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: could not find neighbour cell with PCI=%d\n", rrc_ue_id, neighbour_pci);
return;
}
nr_rrc_trigger_n2_ho(rrc, UE, scell_du->nr_pci, neighbour);
}
+27
View File
@@ -23,6 +23,7 @@
#include <stdint.h>
#include "common/utils/ds/byte_array.h"
#include "nr_rrc_defs.h"
/* forward declarations */
typedef struct gNB_RRC_INST_s gNB_RRC_INST;
@@ -32,6 +33,8 @@ typedef struct nr_rrc_du_container_t nr_rrc_du_container_t;
typedef struct NR_CellGroupConfig NR_CellGroupConfig_t;
typedef void (*ho_cancel_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef int (*ho_status_transfer_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status);
typedef struct nr_ho_source_cu {
/// pointer to the (source) DU structure
const nr_rrc_du_container_t *du;
@@ -47,11 +50,16 @@ typedef struct nr_ho_source_cu {
/// function pointer to announce the handover cancellation, e.g.,
/// reestablishment
ho_cancel_t ho_cancel;
/// status transfer
ho_status_transfer_t ho_status_transfer;
} nr_ho_source_cu_t;
/* acknowledgement of handover request. buf+len is the RRC Reconfiguration */
typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_failure_t)(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
typedef void (*ho_trigger_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef struct nr_ho_target_cu {
/// pointer to the (target) DU structure
const nr_rrc_du_container_t *du;
@@ -61,10 +69,16 @@ typedef struct nr_ho_target_cu {
uint32_t du_ue_id;
/// new (target) RNTI (as for du_ue_id)
rnti_t new_rnti;
/// Handover Preparation Buffer
byte_array_t ue_ho_prep_info;
/// function pointer to trigger handover on target gNB
ho_trigger_t ho_trigger;
/// function pointer to announce handover request acknowledgment
ho_req_ack_t ho_req_ack;
/// function pointer to announce handover success
ho_success_t ho_success;
/// function pointer to announce the handover failure
ho_failure_t ho_failure;
} nr_ho_target_cu_t;
typedef struct nr_handover_context_s {
@@ -72,7 +86,20 @@ typedef struct nr_handover_context_s {
nr_ho_target_cu_t *target;
} nr_handover_context_t;
typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t;
nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type);
void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du);
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue);
void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *ue,
int serving_pci,
const nr_neighbour_cell_t *neighbour_config);
void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len);
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
#endif /* RRC_GNB_MOBILITY_H_ */
+89 -19
View File
@@ -532,19 +532,74 @@ static void nr_rrc_process_sib1(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_inf
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index)
static void nr_rrc_nh_update(NR_UE_RRC_INST_t *rrc, uint8_t *kamf, uint8_t *sync_input)
{
if (rec_1530->fullConfig) {
// TODO perform the full configuration procedure as specified in 5.3.5.11 of 331
LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n");
uint8_t nh[SECURITY_KEY_LEN] = {0};
nr_derive_nh(kamf, sync_input, nh);
log_hex_buffer("Sync input = stored NH", rrc->nh, SECURITY_KEY_LEN);
memcpy(rrc->nh, nh, SECURITY_KEY_LEN);
rrc->nhcc++; // Increase stored nextHopChainingCount
}
/** @brief AS security key update procedure (5.3.5.7 3GPP TS 38.331) */
void as_security_key_update(NR_UE_RRC_INST_t *rrc, NR_MasterKeyUpdate_t *mku)
{
if (mku->nas_Container) {
LOG_E(NR_RRC, "forward the nas-Container to the upper layers: not implemented yet\n");
}
if (rec_1530->masterCellGroup)
nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index);
if (rec_1530->masterKeyUpdate) {
// TODO perform AS security key update procedure as specified in 5.3.5.7
LOG_E(NR_RRC, "RRCReconfiguration includes masterKeyUpdate but this is not implemented yet\n");
if (mku->keySetChangeIndicator) {
LOG_E(NR_RRC, "derive or update the K gNB key based on the K AMF key, as specified in TS 33.501: not implemented yet\n");
} else {
/* derive or update the K gNB key based on the current K gNB key or the NH, using the nextHopChainingCount
value indicated in the received masterKeyUpdate, as specified in 6.9.2.3.3 3GPP TS 33.501 */
if (mku->nextHopChainingCount != rrc->nhcc) {
// - If the UE received an NCC value that was different from the NCC associated with the currently active
// K gNB/K eNB, the UE shall first synchronize the locally kept NH parameter by computing the function defined in
// Annex A.10 iteratively (and increasing the NCC value until it matches the NCC value received from the source
// ng-eNB/gNB via the HO command message.
LOG_A(NR_RRC, "Received masterKeyUpdate (nextHopChainingCount %ld): update security keys\n", mku->nextHopChainingCount);
/** @todo: The KAMF should be obtained from NAS. This exchange over ITTI must be synchronized
* with the rest of the RRCReconfiguration procedure, in particular, the RadioBearerConfig
* processing that triggers bearer modifications. Security configueration of bearers must
* complete using the newly derived keys. As a workaround NAS is directly accessed here. */
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
uint8_t *kamf = nas->security.kamf;
log_hex_buffer("Stored kamf", kamf, SECURITY_KEY_LEN);
if (rrc->nhcc == 0) { // First derivation
derive_kgnb(kamf, 0, rrc->kgnb);
log_hex_buffer("Sync input = derived kgnb", rrc->kgnb, SECURITY_KEY_LEN);
nr_rrc_nh_update(rrc, kamf, rrc->kgnb);
}
for (int i = rrc->nhcc; i < mku->nextHopChainingCount; i++) { // Following derivations
LOG_D(NR_RRC, "Derive keys for ChainingCount = %d\n", i);
nr_rrc_nh_update(rrc, kamf, rrc->nh);
}
nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->nh, rrc->kgnb);
// When the NCC values match, the UE shall compute the K NG-RAN *
// from the synchronized NH parameter and the target PCI and its frequency ARFCN-DL/EARFCN-DL using the
// function defined in Annex A.11 and A.12.
// The UE shall use the KNG-RAN * as the K gNB when communicating with the target gNB and as the KeNB when
// communicating with the target ng-eNB.
} else {
nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb, rrc->kgnb);
}
log_hex_buffer("Derived kgnb", rrc->kgnb, SECURITY_KEY_LEN);
}
/* Check if there is dedicated NAS information to forward to NAS */
}
static nr_pdcp_entity_security_keys_and_algos_t get_security_rrc_parameters(NR_UE_RRC_INST_t *ue, bool cp)
{
nr_pdcp_entity_security_keys_and_algos_t out = {0};
out.ciphering_algorithm = ue->cipheringAlgorithm;
out.integrity_algorithm = ue->integrityProtAlgorithm;
nr_derive_key(cp ? RRC_ENC_ALG : UP_ENC_ALG, ue->cipheringAlgorithm, ue->kgnb, out.ciphering_key);
nr_derive_key(cp ? RRC_INT_ALG : UP_INT_ALG, ue->integrityProtAlgorithm, ue->kgnb, out.integrity_key);
return out;
}
/** @brief Check if there is dedicated NAS information to forward to NAS */
static void nr_rrc_process_dedicatedNAS_MessageList(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530)
{
if (rec_1530->dedicatedNAS_MessageList) {
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *tmp = rec_1530->dedicatedNAS_MessageList;
for (int i = 0; i < tmp->list.count; i++) {
@@ -557,6 +612,21 @@ static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCRe
}
tmp->list.count = 0; // to prevent the automatic free by ASN1_FREE
}
}
static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index)
{
if (rec_1530->fullConfig) {
// TODO perform the full configuration procedure as specified in 5.3.5.11 of 331
LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n");
}
if (rec_1530->masterCellGroup)
nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index);
if (rec_1530->masterKeyUpdate) {
as_security_key_update(rrc, rec_1530->masterKeyUpdate);
nr_pdcp_entity_security_keys_and_algos_t sp = get_security_rrc_parameters(rrc, true);
nr_pdcp_config_set_security(rrc->ue_id, 1, true, &sp);
}
NR_UE_RRC_SI_INFO *SI_info = &rrc->perNB[gNB_index].SInfo;
if (rec_1530->dedicatedSIB1_Delivery) {
NR_SIB1_t *sib1 = NULL;
@@ -633,6 +703,9 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_
case NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration: {
NR_RRCReconfiguration_IEs_t *ie = reconfiguration->criticalExtensions.choice.rrcReconfiguration;
if (ie->nonCriticalExtension)
nr_rrc_process_reconfiguration_v1530(rrc, ie->nonCriticalExtension, gNB_index);
if (ie->radioBearerConfig) {
LOG_I(NR_RRC, "RRCReconfiguration includes radio Bearer Configuration\n");
nr_rrc_ue_process_RadioBearerConfig(rrc, ie->radioBearerConfig);
@@ -640,8 +713,11 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_
xer_fprint(stdout, &asn_DEF_NR_RadioBearerConfig, (const void *)ie->radioBearerConfig);
}
/** @note This triggers PDU Session Establishment Accept which sets up the TUN interface.
* SDAP entity is a pre-requisite, therefore the radioBearerConfig has to be processed
* (in nr_rrc_ue_process_RadioBearerConfig, add_drb) early enough, or it may cause a race condition in SDAP. */
if (ie->nonCriticalExtension)
nr_rrc_process_reconfiguration_v1530(rrc, ie->nonCriticalExtension, gNB_index);
nr_rrc_process_dedicatedNAS_MessageList(rrc, ie->nonCriticalExtension);
if (ie->secondaryCellGroup) {
NR_CellGroupConfig_t *cellGroupConfig = NULL;
@@ -1959,14 +2035,8 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc,
ue_rrc->integrityProtAlgorithm = *radioBearerConfig->securityConfig->securityAlgorithmConfig->integrityProtAlgorithm;
}
}
security_rrc_parameters.ciphering_algorithm = ue_rrc->cipheringAlgorithm;
security_rrc_parameters.integrity_algorithm = ue_rrc->integrityProtAlgorithm;
nr_derive_key(RRC_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, security_rrc_parameters.ciphering_key);
nr_derive_key(RRC_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, security_rrc_parameters.integrity_key);
security_up_parameters.ciphering_algorithm = ue_rrc->cipheringAlgorithm;
security_up_parameters.integrity_algorithm = ue_rrc->integrityProtAlgorithm;
nr_derive_key(UP_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, security_up_parameters.ciphering_key);
nr_derive_key(UP_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, security_up_parameters.integrity_key);
security_rrc_parameters = get_security_rrc_parameters(ue_rrc, true);
security_up_parameters = get_security_rrc_parameters(ue_rrc, false);
}
if (radioBearerConfig->srb_ToAddModList != NULL) {
+4
View File
@@ -248,6 +248,10 @@ typedef struct NR_UE_RRC_INST_s {
e_NR_IntegrityProtAlgorithm integrityProtAlgorithm;
long keyToUse;
bool as_security_activated;
/// Next Hop Chaining Count
uint8_t nh[32];
uint64_t nhcc;
bool detach_after_release;
NR_timer_t release_timer;
NR_RRCRelease_t *RRCRelease;
+159
View File
@@ -66,6 +66,43 @@ void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in)
}
}
ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in)
{
ngap_cause_t out = {0};
switch (in->present) {
case NGAP_Cause_PR_radioNetwork:
out.type = NGAP_CAUSE_RADIO_NETWORK;
out.value = in->choice.radioNetwork;
break;
case NGAP_Cause_PR_transport:
out.type = NGAP_CAUSE_TRANSPORT;
out.value = in->choice.transport;
break;
case NGAP_Cause_PR_nas:
out.type = NGAP_CAUSE_NAS;
out.value = in->choice.nas;
break;
case NGAP_Cause_PR_protocol:
out.type = NGAP_CAUSE_PROTOCOL;
out.value = in->choice.protocol;
break;
case NGAP_Cause_PR_misc:
out.type = NGAP_CAUSE_MISC;
out.value = in->choice.misc;
break;
default:
out.type = NGAP_CAUSE_NOTHING;
NGAP_ERROR("Unknown failure cause %d\n", in->present);
break;
}
return out;
}
nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in)
{
nr_guami_t out = {0};
@@ -112,3 +149,125 @@ ngap_mobility_restriction_t decode_ngap_mobility_restriction(const NGAP_Mobility
TBCD_TO_MCC_MNC(&in->servingPLMN, out.serving_plmn.mcc, out.serving_plmn.mnc, out.serving_plmn.mnc_digit_length);
return out;
}
void encode_ngap_target_id(NGAP_HandoverRequiredIEs_t *out, const target_ran_node_id_t *in)
{
out->id = NGAP_ProtocolIE_ID_id_TargetID;
out->criticality = NGAP_Criticality_reject;
out->value.present = NGAP_HandoverRequiredIEs__value_PR_TargetID;
// CHOICE Target ID: NG-RAN (M)
out->value.choice.TargetID.present = NGAP_TargetID_PR_targetRANNodeID;
asn1cCalloc(out->value.choice.TargetID.choice.targetRANNodeID, targetRan);
// Global RAN Node ID (M)
targetRan->globalRANNodeID.present = NGAP_GlobalRANNodeID_PR_globalGNB_ID;
asn1cCalloc(targetRan->globalRANNodeID.choice.globalGNB_ID, globalGnbId);
globalGnbId->gNB_ID.present = NGAP_GNB_ID_PR_gNB_ID;
MACRO_GNB_ID_TO_BIT_STRING(in->targetgNBId, &globalGnbId->gNB_ID.choice.gNB_ID);
MCC_MNC_TO_PLMNID(in->plmn_identity.mcc, in->plmn_identity.mnc, in->plmn_identity.mnc_digit_length, &globalGnbId->pLMNIdentity);
// Selected TAI (M)
INT24_TO_OCTET_STRING(in->tac, &targetRan->selectedTAI.tAC);
MCC_MNC_TO_PLMNID(in->plmn_identity.mcc,
in->plmn_identity.mnc,
in->plmn_identity.mnc_digit_length,
&targetRan->selectedTAI.pLMNIdentity);
}
void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_t cell_id)
{
out->iE_Extensions = NULL;
MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &out->pLMNIdentity);
NR_CELL_ID_TO_BIT_STRING(cell_id, &out->nRCellIdentity);
}
pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params)
{
pdusession_level_qos_parameter_t out = {0};
// QFI
out.qfi = qfi;
AssertFatal(params != NULL, "QoS parameters are NULL\n");
// QosCharacteristics
const NGAP_QosCharacteristics_t *qosChar = &params->qosCharacteristics;
AssertFatal(qosChar != NULL, "QoS characteristics are NULL\n");
if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) {
AssertFatal(qosChar->choice.nonDynamic5QI != NULL, "nonDynamic5QI is NULL\n");
out.fiveQI_type = NON_DYNAMIC;
out.fiveQI = qosChar->choice.nonDynamic5QI->fiveQI;
} else if (qosChar->present == NGAP_QosCharacteristics_PR_dynamic5QI) {
AssertFatal(qosChar->choice.dynamic5QI != NULL, "dynamic5QI is NULL\n");
out.fiveQI_type = DYNAMIC;
out.fiveQI = *qosChar->choice.dynamic5QI->fiveQI;
} else {
AssertFatal(0, "Unsupported QoS Characteristics present value: %d\n", qosChar->present);
}
// Allocation and Retention Priority
const NGAP_AllocationAndRetentionPriority_t *arp = &params->allocationAndRetentionPriority;
out.allocation_retention_priority.priority_level = arp->priorityLevelARP;
out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability;
out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability;
return out;
}
static gtpu_tunnel_t decode_TNLInformation(const NGAP_GTPTunnel_t *gTPTunnel_p)
{
gtpu_tunnel_t out = {0};
// Transport layer address
memcpy(out.addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size);
out.addr.length = gTPTunnel_p->transportLayerAddress.size - gTPTunnel_p->transportLayerAddress.bits_unused;
// GTP tunnel endpoint ID
OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, out.teid);
return out;
}
void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf)
{
void *decoded = NULL;
asn_codec_ctx_t ctx = {.max_stack_size = 100 * 1000};
asn_dec_rval_t rval = aper_decode(&ctx, td, &decoded, buf.buf, buf.size, 0, 0);
if (rval.code != RC_OK) {
NGAP_ERROR("Decoding failed for %s\n", td->name);
return NULL;
}
return decoded;
}
/** @brief Decode PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in)
{
void *decoded = decode_pdusession_transfer(&asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, in);
if (!decoded) {
LOG_E(NR_RRC, "Failed to decode PDUSessionResourceSetupRequestTransfer\n");
return false;
}
NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer = (NGAP_PDUSessionResourceSetupRequestTransfer_t *)decoded;
for (int i = 0; i < pdusessionTransfer->protocolIEs.list.count; i++) {
NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[i];
switch (pdusessionTransfer_ies->id) {
// UL NG-U UP TNL Information (Mandatory)
case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation:
out->n3_incoming = decode_TNLInformation(pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel);
break;
// PDU Session Type (Mandatory)
case NGAP_ProtocolIE_ID_id_PDUSessionType:
out->pdu_session_type = pdusessionTransfer_ies->value.choice.PDUSessionType;
break;
// QoS Flow Setup Request List (Mandatory)
case NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList:
out->nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count;
for (int i = 0; i < out->nb_qos; i++) {
NGAP_QosFlowSetupRequestItem_t *item = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[i];
out->qos[i] = fill_qos(item->qosFlowIdentifier, &item->qosFlowLevelQosParameters);
}
break;
default:
LOG_D(NR_RRC, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id);
}
}
ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, pdusessionTransfer);
return true;
}
+6
View File
@@ -83,10 +83,16 @@ typedef int (*ngap_message_decoded_callback)(sctp_assoc_t assoc_id, uint32_t str
void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in);
nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in);
ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in);
ngap_ambr_t decode_ngap_UEAggregateMaximumBitRate(const NGAP_UEAggregateMaximumBitRate_t *in);
nssai_t decode_ngap_nssai(const NGAP_S_NSSAI_t *in);
ngap_security_capabilities_t decode_ngap_security_capabilities(const NGAP_UESecurityCapabilities_t *in);
ngap_mobility_restriction_t decode_ngap_mobility_restriction(const NGAP_MobilityRestrictionList_t *in);
void encode_ngap_target_id(NGAP_HandoverRequiredIEs_t *out, const target_ran_node_id_t *in);
void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_t cell_id);
pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params);
void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf);
bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in);
/** @}*/
+290
View File
@@ -55,6 +55,8 @@
#include "ngap_gNB_management_procedures.h"
#include "ngap_gNB_nas_procedures.h"
#include "ngap_messages_types.h"
#include "ngap_gNB_mobility_management.h"
#include "ngap_gNB_ue_context.h"
#include "oai_asn1.h"
#include "openair3/SECU/kdf.h"
#include "queue.h"
@@ -275,12 +277,273 @@ void ngap_gNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) {
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
}
/** @brief UE Mobility Management: callback for Handover Required */
int ngap_handover_required(instance_t instance, ngap_handover_required_t *msg)
{
DevAssert(msg != NULL);
NGAP_DEBUG("Triggered Handover Required\n");
/* Retrieve the NGAP gNB instance associated with Mod_id */
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
DevAssert(ngap_gNB_instance_p != NULL);
ngap_gNB_ue_context_t *ue_context_p = NULL;
if ((ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id)) == NULL) {
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", msg->gNB_ue_ngap_id);
return -1;
}
if ((ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id)) {
NGAP_ERROR("ue_context_p->gNB_ue_ngap_id %d does not match msg->gNB_ue_ngap_id %d\n",
ue_context_p->gNB_ue_ngap_id,
msg->gNB_ue_ngap_id);
return -1;
}
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_required(msg);
if (!pdu) {
NGAP_ERROR("Failed to encode Handover Required\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, &pdu);
byte_array_t out = { .buf = NULL, .len = 0 };
if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) {
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
NGAP_ERROR("Failed to encode Handover Required message\n");
return -1;
}
free(pdu);
/* UE associated signalling -> use the allocated stream */
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
ue_context_p->amf_ref->assoc_id,
out.buf,
out.len,
ue_context_p->tx_stream);
return 0;
}
void ngap_gNB_init(void) {
NGAP_DEBUG("Starting NGAP layer\n");
ngap_gNB_prepare_internal_data();
itti_mark_task_ready(TASK_NGAP);
}
static int ngap_gNB_handover_failure(instance_t instance, const ngap_handover_failure_t *msg)
{
ngap_gNB_ue_context_t *ue_context_p = NULL;
uint8_t *buffer = NULL;
uint32_t length;
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
DevAssert(msg != NULL);
DevAssert(ngap_gNB_instance_p != NULL);
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_failure(msg);
if (!pdu) {
NGAP_ERROR("Failed to encode NG Handover Failure\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
if (ngap_gNB_encode_pdu(pdu, &buffer, &length) < 0) {
NGAP_ERROR("Failed to encode HANDOVER FAILURE MESSAGE\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
ngap_gNB_amf_data_t *amf = ngap_gNB_get_AMF_from_instance(ngap_gNB_instance_p);
if ((ue_context_p = ngap_get_ue_context_from_amf_ue_ngap_id(msg->amf_ue_ngap_id)) == NULL) {
//The context for this gNB ue ngap id doesn't exist in the map of gNB UEs
NGAP_WARN("Failed to find UE context associated with amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id);
/*In this case there is no ue context since HO REQ might be failed at preprocessing stage itself.
* so sctp message send to AMF based on its assoc_id */
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, amf->assoc_id, buffer, length, 0);
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, amf->assoc_id, buffer, length, amf->nextstream);
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, &pdu);
return 0;
}
static int ngap_gNB_handover_request_acknowledge(instance_t instance, ngap_handover_request_ack_t *msg)
{
byte_array_t ba = { .buf = NULL, .len = 0 };
/* Retrieve the NGAP gNB instance associated with Mod_id */
ngap_gNB_instance_t *ngap = ngap_gNB_get_instance(instance);
DevAssert(msg != NULL);
DevAssert(ngap != NULL);
// First, create and store NGAP UE context
ngap_gNB_ue_context_t ue_context_p = {
.amf_ref = ngap_gNB_get_AMF_from_instance(ngap),
.gNB_ue_ngap_id = msg->gNB_ue_ngap_id,
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
.gNB_instance = ngap,
.ue_state = NGAP_UE_CONNECTED,
};
if (ue_context_p.amf_ref == NULL) {
NGAP_ERROR("Failed to fetch AMF for current NGAP instance\n");
return -1;
}
ngap_store_ue_context(&ue_context_p);
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_request_ack(msg);
if (!pdu) {
NGAP_ERROR("Failed to encode NG Handover Request Acknowledge\n");
return -1;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, pdu);
if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) {
NGAP_ERROR("Failed to encode Handover Request Acknowledge message\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
/* UE associated signalling -> use the allocated stream */
ngap_gNB_itti_send_sctp_data_req(ngap->instance, ue_context_p.amf_ref->assoc_id, ba.buf, ba.len, ue_context_p.tx_stream);
NGAP_INFO("Sent Handover Request Acknowledge to AMF\n");
return 0;
}
static int ngap_gNB_handover_notify(instance_t instance, const ngap_handover_notify_t *msg)
{
LOG_D(NGAP, "Encode NGAP Handover Notify\n");
ngap_gNB_ue_context_t *ue_context_p = NULL;
byte_array_t ba = { .buf = NULL, .len = 0};
/* Retrieve the NGAP gNB instance associated with Mod_id */
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
DevAssert(msg != NULL);
DevAssert(ngap_gNB_instance_p != NULL);
if ((ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id)) == NULL) {
NGAP_ERROR("Failed to encode Handover Notify: no ue context associated with gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
return -1;
}
if (ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id) {
NGAP_ERROR("Failed to encode Handover Notify: unknown gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
return -1;
}
if (ue_context_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
NGAP_ERROR("Failed to encode Handover Notify: unknown amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id);
return -1;
}
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_notify(msg);
if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) {
NGAP_ERROR("Failed to encode NG Handover Notify message\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
/* UE associated signalling -> use the allocated stream */
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
ue_context_p->amf_ref->assoc_id,
ba.buf,
ba.len,
ue_context_p->tx_stream);
return 0;
}
/** @brief NGAP: Handover Cancel callback */
static int ngap_gNB_handover_cancel(instance_t instance, const ngap_handover_cancel_t *msg)
{
DevAssert(msg != NULL);
byte_array_t ba = {.buf = NULL, .len = 0};
// Retrieve NGAP gNB instance
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
DevAssert(ngap_gNB_instance_p != NULL);
// we must have the UE context by RAN UE NGAP ID
ngap_gNB_ue_context_t *ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
if (ue_context_p == NULL) {
NGAP_ERROR("Failed to encode Handover Cancel: no UE context for gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
return -1;
}
if (ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id) {
NGAP_ERROR("Failed to encode Handover Cancel: mismatched gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
return -1;
}
if (ue_context_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
NGAP_ERROR("Failed to encode Handover Cancel: mismatched amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id);
return -1;
}
/* Encode NGAP PDU */
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_cancel(msg);
if (pdu == NULL) {
NGAP_ERROR("Failed to encode NG Handover Cancel\n");
return -1;
}
if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) {
NGAP_ERROR("Failed to encode NG Handover Cancel message PDU\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
/* UE-associated signalling -> use the UE's allocated SCTP stream */
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
ue_context_p->amf_ref->assoc_id,
ba.buf,
ba.len,
ue_context_p->tx_stream);
return 0;
}
int ngap_gNB_handle_ul_ran_status_transfer(instance_t instance, const ngap_ran_status_transfer_t *msg)
{
NGAP_DEBUG("Triggered UL RAN Status Transfer\n");
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
DevAssert(ngap_gNB_instance_p != NULL);
ngap_gNB_ue_context_t *ue_context_p = ngap_get_ue_context(msg->gnb_ue_ngap_id);
if (!ue_context_p || ue_context_p->gNB_ue_ngap_id != msg->gnb_ue_ngap_id) {
NGAP_ERROR("Could not find UE context for gNB_ue_ngap_id %d\n", msg->gnb_ue_ngap_id);
return -1;
}
NGAP_NGAP_PDU_t *pdu = encode_ng_ul_ran_status_transfer(msg);
if (!pdu) {
NGAP_ERROR("Failed to encode UL RAN Status Transfer\n");
return -1;
}
byte_array_t out = {.buf = NULL, .len = 0};
if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) {
NGAP_ERROR("UL RAN Status Transfer encoding failure\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
ngap_gNB_itti_send_sctp_data_req(instance, ue_context_p->amf_ref->assoc_id, out.buf, out.len, ue_context_p->tx_stream);
return 0;
}
void *ngap_gNB_process_itti_msg(void *notUsed) {
MessageDef *received_msg = NULL;
int result;
@@ -356,6 +619,33 @@ void *ngap_gNB_process_itti_msg(void *notUsed) {
ngap_gNB_pdusession_release_resp(instance, &NGAP_PDUSESSION_RELEASE_RESPONSE(received_msg));
break;
case NGAP_HANDOVER_REQUIRED:
if (ngap_handover_required(instance, &NGAP_HANDOVER_REQUIRED(received_msg)) < 0) {
NGAP_ERROR("Handover Required failure: indication to RRC is not sent!\n");
}
break;
case NGAP_HANDOVER_FAILURE:
ngap_gNB_handover_failure(instance, &NGAP_HANDOVER_FAILURE(received_msg));
break;
case NGAP_HANDOVER_REQUEST_ACKNOWLEDGE:
ngap_gNB_handover_request_acknowledge(instance, &NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(received_msg));
free_ng_handover_req_ack(&NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(received_msg));
break;
case NGAP_HANDOVER_NOTIFY:
ngap_gNB_handover_notify(instance, &NGAP_HANDOVER_NOTIFY(received_msg));
break;
case NGAP_HANDOVER_CANCEL:
ngap_gNB_handover_cancel(instance, &NGAP_HANDOVER_CANCEL(received_msg));
break;
case NGAP_UL_RAN_STATUS_TRANSFER:
ngap_gNB_handle_ul_ran_status_transfer(instance, &NGAP_UL_RAN_STATUS_TRANSFER(received_msg));
break;
default:
NGAP_ERROR("Received unhandled message: %d:%s\n", ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
break;
+22
View File
@@ -70,6 +70,12 @@ static int ngap_gNB_decode_initiating_message(NGAP_NGAP_PDU_t *pdu) {
NGAP_INFO("PDUSESSIONSetup initiating message\n");
break;
case NGAP_ProcedureCode_id_HandoverPreparation:
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
free(res.buffer);
NGAP_INFO("Handover Preparation initiating message\n");
break;
case NGAP_ProcedureCode_id_PDUSessionResourceModify:
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
free(res.buffer);
@@ -88,6 +94,18 @@ static int ngap_gNB_decode_initiating_message(NGAP_NGAP_PDU_t *pdu) {
NGAP_INFO("TODO ErrorIndication initiating message\n");
break;
case NGAP_ProcedureCode_id_HandoverResourceAllocation:
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
free(res.buffer);
NGAP_INFO("Handover Resource Allocation initiating message\n");
break;
case NGAP_ProcedureCode_id_DownlinkRANStatusTransfer:
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
free(res.buffer);
NGAP_INFO("DL RAN Status Transfer initiating message\n");
break;
default:
NGAP_ERROR("Unknown procedure ID (%d) for initiating message\n",
(int)pdu->choice.initiatingMessage->procedureCode);
@@ -119,6 +137,10 @@ static int ngap_gNB_decode_successful_outcome(NGAP_NGAP_PDU_t *pdu) {
free(res.buffer);
break;
case NGAP_ProcedureCode_id_HandoverPreparation:
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
free(res.buffer);
break;
default:
NGAP_ERROR("Unknown procedure ID (%d) for successfull outcome message\n",
+17 -4
View File
@@ -52,7 +52,10 @@ static inline int ngap_gNB_encode_initiating(NGAP_NGAP_PDU_t *pdu, uint8_t **buf
NGAP_ProcedureCode_id_NASNonDeliveryIndication,
NGAP_ProcedureCode_id_UEContextReleaseRequest,
NGAP_ProcedureCode_id_PathSwitchRequest,
NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication};
NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication,
NGAP_ProcedureCode_id_UplinkRANStatusTransfer,
NGAP_ProcedureCode_id_HandoverPreparation,
NGAP_ProcedureCode_id_HandoverNotification};
int i;
for (i = 0; i < sizeofArray(tmp); i++)
if (pdu->choice.initiatingMessage->procedureCode == tmp[i])
@@ -76,7 +79,8 @@ static inline int ngap_gNB_encode_successfull_outcome(NGAP_NGAP_PDU_t *pdu, uint
NGAP_ProcedureCode_id_UEContextRelease,
NGAP_ProcedureCode_id_PDUSessionResourceSetup,
NGAP_ProcedureCode_id_PDUSessionResourceModify,
NGAP_ProcedureCode_id_PDUSessionResourceRelease};
NGAP_ProcedureCode_id_PDUSessionResourceRelease,
NGAP_ProcedureCode_id_HandoverResourceAllocation};
int i;
for (i = 0; i < sizeofArray(tmp); i++)
if (pdu->choice.successfulOutcome->procedureCode == tmp[i])
@@ -97,8 +101,17 @@ static inline int ngap_gNB_encode_unsuccessfull_outcome(NGAP_NGAP_PDU_t *pdu, ui
{
DevAssert(pdu != NULL);
if (pdu->choice.unsuccessfulOutcome->procedureCode != NGAP_ProcedureCode_id_InitialContextSetup) {
NGAP_DEBUG("Unknown procedure ID (%d) for unsuccessfull outcome message\n", (int)pdu->choice.unsuccessfulOutcome->procedureCode);
const NGAP_ProcedureCode_t id[] = {NGAP_ProcedureCode_id_InitialContextSetup, NGAP_ProcedureCode_id_HandoverResourceAllocation};
int i;
for (i = 0; i < sizeofArray(id); i++) {
if (pdu->choice.unsuccessfulOutcome->procedureCode == id[i]) {
break;
}
}
if (i == sizeofArray(id)) {
NGAP_WARN("Unknown procedure ID (%ld) for successful outcome message\n", pdu->choice.successfulOutcome->procedureCode);
return -1;
}
+221 -88
View File
@@ -44,7 +44,9 @@
#include "ngap_common.h"
#include "ngap_gNB_decoder.h"
#include "ngap_gNB_defs.h"
#include "ngap_gNB_nnsf.h"
#include "ngap_gNB_management_procedures.h"
#include "ngap_gNB_mobility_management.h"
#include "ngap_gNB_nas_procedures.h"
#include "ngap_gNB_trace.h"
#include "ngap_gNB_ue_context.h"
@@ -693,97 +695,40 @@ static int ngap_gNB_handle_error_indication(sctp_assoc_t assoc_id, uint32_t stre
return 0;
}
static void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf)
/** @brief Handler for NGAP Handover Request message (AMF -> target NG-RAN)
* Decode the NGAP message and transfer to RRC */
static int ngap_gNB_handle_handover_request(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
void *decoded = NULL;
asn_codec_ctx_t ctx = {.max_stack_size = 100 * 1000};
asn_dec_rval_t rval = aper_decode(&ctx, td, &decoded, buf.buf, buf.size, 0, 0);
if (rval.code != RC_OK) {
NGAP_ERROR("Decoding failed for %s\n", td->name);
return NULL;
}
return decoded;
}
DevAssert(pdu != NULL);
static pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params)
{
pdusession_level_qos_parameter_t out = {0};
// QFI
out.qfi = qfi;
AssertFatal(params != NULL, "QoS parameters are NULL\n");
// QosCharacteristics
const NGAP_QosCharacteristics_t *qosChar = &params->qosCharacteristics;
AssertFatal(qosChar != NULL, "QoS characteristics are NULL\n");
if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) {
AssertFatal(qosChar->choice.nonDynamic5QI != NULL, "nonDynamic5QI is NULL\n");
out.fiveQI_type = NON_DYNAMIC;
out.fiveQI = qosChar->choice.nonDynamic5QI->fiveQI;
} else if (qosChar->present == NGAP_QosCharacteristics_PR_dynamic5QI) {
AssertFatal(qosChar->choice.dynamic5QI != NULL, "dynamic5QI is NULL\n");
out.fiveQI_type = DYNAMIC;
out.fiveQI = *qosChar->choice.dynamic5QI->fiveQI;
} else {
AssertFatal(0, "Unsupported QoS Characteristics present value: %d\n", qosChar->present);
}
// Allocation and Retention Priority
const NGAP_AllocationAndRetentionPriority_t *arp = &params->allocationAndRetentionPriority;
out.allocation_retention_priority.priority_level = arp->priorityLevelARP;
out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability;
out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability;
return out;
}
static gtpu_tunnel_t decode_TNLInformation(const NGAP_GTPTunnel_t *gTPTunnel_p)
{
gtpu_tunnel_t out = {0};
// Transport layer address
memcpy(out.addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size);
out.addr.length = gTPTunnel_p->transportLayerAddress.size - gTPTunnel_p->transportLayerAddress.bits_unused;
// GTP tunnel endpoint ID
OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, out.teid);
return out;
}
/** @brief Decode PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */
static bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in)
{
void *decoded = decode_pdusession_transfer(&asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, in);
if (!decoded) {
LOG_E(NR_RRC, "Failed to decode PDUSessionResourceSetupRequestTransfer\n");
return false;
ngap_gNB_amf_data_t *amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0);
if (amf_desc_p == NULL) {
NGAP_ERROR("Received Handover Request for non existing AMF context\n");
return -1;
}
NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer = (NGAP_PDUSessionResourceSetupRequestTransfer_t *)decoded;
for (int i = 0; i < pdusessionTransfer->protocolIEs.list.count; i++) {
NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[i];
switch (pdusessionTransfer_ies->id) {
// UL NG-U UP TNL Information (Mandatory)
case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation:
out->n3_incoming = decode_TNLInformation(pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel);
break;
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_REQUEST);
ngap_handover_request_t *msg = &NGAP_HANDOVER_REQUEST(message_p);
memset(msg, 0, sizeof(*msg));
// PDU Session Type (Mandatory)
case NGAP_ProtocolIE_ID_id_PDUSessionType:
out->pdu_session_type = pdusessionTransfer_ies->value.choice.PDUSessionType;
break;
// QoS Flow Setup Request List (Mandatory)
case NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList:
out->nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count;
for (int i = 0; i < out->nb_qos; i++) {
NGAP_QosFlowSetupRequestItem_t *item = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[i];
out->qos[i] = fill_qos(item->qosFlowIdentifier, &item->qosFlowLevelQosParameters);
}
break;
default:
LOG_D(NR_RRC, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id);
}
if (decode_ng_handover_request(msg, pdu) < 0) {
NGAP_ERROR("Failed to decode NG Handover Request\n");
ngap_handover_failure_t fail = {
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM,
};
NGAP_INFO("Send NG Handover Failure message (amf_ue_ngap_id %ld) with cause %d \n ", fail.amf_ue_ngap_id, fail.cause.value);
MessageDef *msg_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_FAILURE);
NGAP_HANDOVER_FAILURE(msg_p) = fail;
itti_send_msg_to_task(TASK_NGAP, amf_desc_p->ngap_gNB_instance->instance, msg_p);
return -1;
}
ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, pdusessionTransfer);
return true;
NGAP_INFO("Received NG Handover Request from AMF %s (ID=%lu)\n", amf_desc_p->amf_name, msg->amf_ue_ngap_id);
itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p);
return 0;
}
static int ngap_gNB_handle_initial_context_request(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
@@ -1052,6 +997,100 @@ static int ngap_gNB_handle_pdusession_setup_request(sctp_assoc_t assoc_id, uint3
return 0;
}
/** @brief Handler for the NG Handover Command
* AMF -> source NG-RAN */
static int ngap_gNB_handle_handover_command(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
NGAP_INFO("Received NG Handover Command\n");
ngap_gNB_amf_data_t *amf_desc_p = NULL;
DevAssert(pdu != NULL);
if ((amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0)) == NULL) {
NGAP_ERROR(
"[SCTP %u] Received Handover Command for non "
"existing AMF context\n",
assoc_id);
return -1;
}
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_COMMAND);
ngap_handover_command_t *msg = &NGAP_HANDOVER_COMMAND(message_p);
memset(msg, 0, sizeof(*msg));
if (decode_ng_handover_command(msg, pdu) < 0) {
NGAP_ERROR("Failed to decode NG Handover Command");
free_ng_handover_command(msg);
itti_free(TASK_NGAP, message_p);
return -1;
}
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
if (!ue_desc_p) {
NGAP_ERROR(
"[SCTP %u] Received HO Command Msg for non existing UE context (gNB_ue_ngap_id %d) \n",
assoc_id,
msg->gNB_ue_ngap_id);
free_ng_handover_command(msg);
itti_free(TASK_NGAP, message_p);
return -1;
}
ue_desc_p->rx_stream = stream;
if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
NGAP_ERROR("UE context amf_ue_ngap_id is different form that of the message (%ld != %ld)",
ue_desc_p->amf_ue_ngap_id,
msg->amf_ue_ngap_id);
free_ng_handover_command(msg);
itti_free(TASK_NGAP, message_p);
return -1;
}
NGAP_DEBUG("Handover Command is parsed with a size of: %ld. Sending to RRC GNB!\n", msg->handoverCommand.len);
itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p);
return 0;
}
/** @brief Handler for NG Handover Cancel Acknowledge */
static int ngap_gNB_handle_handover_cancel_ack(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
NGAP_INFO("Received NG Handover Cancel Acknowledge\n");
ngap_gNB_amf_data_t *amf_desc_p = NULL;
DevAssert(pdu != NULL);
amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0);
if (amf_desc_p == NULL) {
NGAP_ERROR("[SCTP %u] Received Handover Cancel Ack for non-existing AMF context\n", assoc_id);
return -1;
}
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_CANCEL_ACK);
ngap_handover_cancel_ack_t *msg = &NGAP_HANDOVER_CANCEL_ACK(message_p);
if (decode_ng_handover_cancel_ack(msg, pdu) < 0) {
NGAP_ERROR("Failed to decode NG Handover Cancel Acknowledge\n");
itti_free(TASK_NGAP, message_p);
return -1;
}
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
if (!ue_desc_p) {
NGAP_ERROR("[SCTP %u] Handover Cancel Ack for unknown UE context (gNB_ue_ngap_id %d)\n", assoc_id, msg->gNB_ue_ngap_id);
itti_free(TASK_NGAP, message_p);
return -1;
}
if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
NGAP_WARN("AMF UE NGAP ID mismatch in HO Cancel Ack (ctx %ld != msg %ld)", ue_desc_p->amf_ue_ngap_id, msg->amf_ue_ngap_id);
}
ue_desc_p->rx_stream = stream;
// cancel was already handled at RRC level, do not forward
return 0;
}
static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
ngap_gNB_amf_data_t *amf_desc_p = NULL;
@@ -1379,6 +1418,100 @@ static int ngap_gNB_handle_ng_ENDC_pdusession_modification_confirm(sctp_assoc_t
return 0;
}
static int ngap_gNB_handle_dl_ran_status_transfer(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
DevAssert(pdu != NULL);
NGAP_DownlinkRANStatusTransfer_t *container = &pdu->choice.initiatingMessage->value.choice.DownlinkRANStatusTransfer;
NGAP_DownlinkRANStatusTransferIEs_t *ie = NULL;
ngap_gNB_amf_data_t *amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0);
if (!amf_desc_p) {
NGAP_ERROR("[SCTP %u] Received DL RAN Status Transfer for unknown AMF\n", assoc_id);
return -1;
}
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id = 0;
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkRANStatusTransferIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
asn_INTEGER2ulong(&ie->value.choice.AMF_UE_NGAP_ID, &amf_ue_ngap_id);
// RAN UE NGAP ID
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkRANStatusTransferIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
NGAP_RAN_UE_NGAP_ID_t gnb_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(gnb_ue_ngap_id);
if (!ue_desc_p) {
NGAP_ERROR("No UE context for gNB_ue_ngap_id %lu\n", gnb_ue_ngap_id);
return -1;
}
ue_desc_p->rx_stream = stream;
if (ue_desc_p->amf_ue_ngap_id != amf_ue_ngap_id)
NGAP_WARN("UE context mismatch: amf_ue_ngap_id (%lu != %lu)\n", ue_desc_p->amf_ue_ngap_id, amf_ue_ngap_id);
// Transparent container
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkRANStatusTransferIEs_t,
ie,
container,
NGAP_ProtocolIE_ID_id_RANStatusTransfer_TransparentContainer,
true);
NGAP_RANStatusTransfer_TransparentContainer_t *tcont = &ie->value.choice.RANStatusTransfer_TransparentContainer;
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_DL_RAN_STATUS_TRANSFER);
ngap_ran_status_transfer_t *msg = &NGAP_DL_RAN_STATUS_TRANSFER(message_p);
memset(msg, 0, sizeof(*msg));
msg->amf_ue_ngap_id = amf_ue_ngap_id;
msg->gnb_ue_ngap_id = gnb_ue_ngap_id;
int nb = tcont->dRBsSubjectToStatusTransferList.list.count;
for (int i = 0; i < nb; ++i) {
NGAP_DRBsSubjectToStatusTransferItem_t *item = tcont->dRBsSubjectToStatusTransferList.list.array[i];
ngap_drb_status_t *s = &msg->ran_status.drb_status_list[i];
s->drb_id = item->dRB_ID;
// UL COUNT
switch (item->dRBStatusUL.present) {
case NGAP_DRBStatusUL_PR_dRBStatusUL18:
s->ul_count.sn_len = NGAP_SN_LENGTH_18;
s->ul_count.pdcp_sn = item->dRBStatusUL.choice.dRBStatusUL18->uL_COUNTValue.pDCP_SN18;
s->ul_count.hfn = item->dRBStatusUL.choice.dRBStatusUL18->uL_COUNTValue.hFN_PDCP_SN18;
break;
case NGAP_DRBStatusUL_PR_dRBStatusUL12:
s->ul_count.sn_len = NGAP_SN_LENGTH_12;
s->ul_count.pdcp_sn = item->dRBStatusUL.choice.dRBStatusUL12->uL_COUNTValue.pDCP_SN12;
s->ul_count.hfn = item->dRBStatusUL.choice.dRBStatusUL12->uL_COUNTValue.hFN_PDCP_SN12;
break;
default:
NGAP_ERROR("Unknown dRBStatusUL.present=%d\n", item->dRBStatusUL.present);
break;
}
// DL COUNT
switch (item->dRBStatusDL.present) {
case NGAP_DRBStatusDL_PR_dRBStatusDL18:
s->dl_count.sn_len = NGAP_SN_LENGTH_18;
s->dl_count.pdcp_sn = item->dRBStatusDL.choice.dRBStatusDL18->dL_COUNTValue.pDCP_SN18;
s->dl_count.hfn = item->dRBStatusDL.choice.dRBStatusDL18->dL_COUNTValue.hFN_PDCP_SN18;
break;
case NGAP_DRBStatusDL_PR_dRBStatusDL12:
s->dl_count.sn_len = NGAP_SN_LENGTH_12;
s->dl_count.pdcp_sn = item->dRBStatusDL.choice.dRBStatusDL12->dL_COUNTValue.pDCP_SN12;
s->dl_count.hfn = item->dRBStatusDL.choice.dRBStatusDL12->dL_COUNTValue.hFN_PDCP_SN12;
break;
default:
NGAP_ERROR("Unknown dRBStatusDL.present=%d\n", item->dRBStatusDL.present);
break;
}
msg->ran_status.nb_drb++;
}
itti_send_msg_to_task(TASK_RRC_GNB, ue_desc_p->gNB_instance->instance, message_p);
return 0;
}
/* Handlers matrix. Only gNB related procedure present here */
const ngap_message_decoded_callback ngap_messages_callback[][3] = {
{0, 0, 0}, /* AMFConfigurationUpdate */
@@ -1388,13 +1521,13 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = {
{ngap_gNB_handle_nas_downlink, 0, 0}, /* DownlinkNASTransport */
{0, 0, 0}, /* DownlinkNonUEAssociatedNRPPaTransport */
{0, 0, 0}, /* DownlinkRANConfigurationTransfer */
{0, 0, 0}, /* DownlinkRANStatusTransfer */
{ngap_gNB_handle_dl_ran_status_transfer, 0, 0}, /* DownlinkRANStatusTransfer */
{0, 0, 0}, /* DownlinkUEAssociatedNRPPaTransport */
{ngap_gNB_handle_error_indication, 0, 0}, /* ErrorIndication */
{0, 0, 0}, /* HandoverCancel */
{0, ngap_gNB_handle_handover_cancel_ack, 0}, /* HandoverCancel */
{0, 0, 0}, /* HandoverNotification */
{0, 0, 0}, /* HandoverPreparation */
{0, 0, 0}, /* HandoverResourceAllocation */
{0, ngap_gNB_handle_handover_command, 0}, /* HandoverPreparation */
{ngap_gNB_handle_handover_request, 0, 0}, /* HandoverResourceAllocation */
{ngap_gNB_handle_initial_context_request, 0, 0}, /* InitialContextSetup */
{0, 0, 0}, /* InitialUEMessage */
{0, 0, 0}, /* LocationReportingControl */
@@ -200,3 +200,11 @@ void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in)
out->bits_unused = 0;
}
}
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in)
{
if (in.size) {
memcpy(out->buffer, in.buf, in.size);
out->length = in.size;
}
}
@@ -50,4 +50,6 @@ void ngap_gNB_prepare_internal_data(void);
void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in);
void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in);
#endif /* NGAP_GNB_MANAGEMENT_PROCEDURES_H_ */
@@ -0,0 +1,748 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "conversions.h"
#include "ngap_common.h"
#include "ngap_msg_includes.h"
#include "ngap_gNB_defs.h"
#include "ngap_gNB_ue_context.h"
#include "oai_asn1.h"
#include "ngap_gNB_management_procedures.h"
#include "ngap_gNB_encoder.h"
#include "ngap_gNB_itti_messaging.h"
#include "conversions.h"
/** @brief UE Mobility Management: encode Handover Required
* (9.2.3.1 of 3GPP TS 38.413) NG-RAN node → AMF */
NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg)
{
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
/* Prepare the NGAP message to encode */
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
asn1cCalloc(pdu->choice.initiatingMessage, head);
head->procedureCode = NGAP_ProcedureCode_id_HandoverPreparation;
head->criticality = NGAP_Criticality_reject;
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverRequired;
NGAP_HandoverRequired_t *out = &head->value.choice.HandoverRequired;
// AMF UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie1);
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
ie1->criticality = NGAP_Criticality_reject;
ie1->value.present = NGAP_HandoverRequiredIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
// RAN UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie2);
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
ie2->criticality = NGAP_Criticality_reject;
ie2->value.present = NGAP_HandoverRequiredIEs__value_PR_RAN_UE_NGAP_ID;
ie2->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
// Handover Type (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie3);
ie3->id = NGAP_ProtocolIE_ID_id_HandoverType;
ie3->criticality = NGAP_Criticality_reject;
ie3->value.present = NGAP_HandoverRequiredIEs__value_PR_HandoverType;
ie3->value.choice.HandoverType = msg->handoverType;
// Cause (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie4);
ie4->id = NGAP_ProtocolIE_ID_id_Cause;
ie4->criticality = NGAP_Criticality_ignore;
ie4->value.present = NGAP_HandoverRequiredIEs__value_PR_Cause;
encode_ngap_cause(&ie4->value.choice.Cause, &msg->cause);
// Target ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie5);
encode_ngap_target_id(ie5, &msg->target_gnb_id);
// PDU Session Resource List (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie6);
ie6->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceListHORqd;
ie6->criticality = NGAP_Criticality_reject;
ie6->value.present = NGAP_HandoverRequiredIEs__value_PR_PDUSessionResourceListHORqd;
for (int i = 0; i < msg->nb_of_pdusessions; ++i) {
asn1cSequenceAdd(ie6->value.choice.PDUSessionResourceListHORqd.list, NGAP_PDUSessionResourceItemHORqd_t, hoRequiredPduSession);
// PDU Session ID (M)
hoRequiredPduSession->pDUSessionID = msg->pdusessions[i].pdusession_id;
// Handover Required Transfer (M)
NGAP_HandoverRequiredTransfer_t hoRequiredTransfer = {0};
uint8_t ho_req_transfer_transparent_container_buffer[128] = {0};
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NGAP_HandoverRequiredTransfer, &hoRequiredTransfer);
asn_enc_rval_t enc_rval = aper_encode_to_buffer(&asn_DEF_NGAP_HandoverRequiredTransfer,
NULL,
&hoRequiredTransfer,
ho_req_transfer_transparent_container_buffer,
128);
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
hoRequiredPduSession->handoverRequiredTransfer.buf = CALLOC(1, (enc_rval.encoded + 7) / 8);
memcpy(hoRequiredPduSession->handoverRequiredTransfer.buf,
ho_req_transfer_transparent_container_buffer,
(enc_rval.encoded + 7) / 8);
hoRequiredPduSession->handoverRequiredTransfer.size = (enc_rval.encoded + 7) / 8;
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_HandoverRequiredTransfer, &hoRequiredTransfer);
}
// Source NG-RAN Node to Target NG-RAN Node Transparent Container (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_SourceToTarget_TransparentContainer;
ie->criticality = NGAP_Criticality_reject;
ie->value.present = NGAP_HandoverRequiredIEs__value_PR_SourceToTarget_TransparentContainer;
NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer_t *source2target = calloc_or_fail(1, sizeof(*source2target));
// RRC Container (M) (HandoverPreparationInformation)
source2target->rRCContainer.size = msg->source2target->handoverInfo.len;
source2target->rRCContainer.buf = malloc_or_fail(msg->source2target->handoverInfo.len);
memcpy(source2target->rRCContainer.buf, msg->source2target->handoverInfo.buf, source2target->rRCContainer.size);
// PDU Session Resource Information List (O)
asn1cCalloc(source2target->pDUSessionResourceInformationList, pduSessionList);
for (uint8_t i = 0; i < msg->nb_of_pdusessions; ++i) {
const pdusession_resource_t *pduSession = &msg->pdusessions[i];
NGAP_DEBUG("Handover Required: preparing PDU Session Resource Information List for PDU Session ID %d\n",
pduSession->pdusession_id);
asn1cSequenceAdd(pduSessionList->list, NGAP_PDUSessionResourceInformationItem_t, item);
// PDU Session ID (M)
item->pDUSessionID = msg->source2target->pdu_session_resource[i].pdusession_id;
// QoS Flow Information List (M)
for (int q = 0; q < msg->source2target->pdu_session_resource[i].nb_of_qos_flow; ++q) {
asn1cSequenceAdd(item->qosFlowInformationList.list, NGAP_QosFlowInformationItem_t, qosFlowInfo);
qosFlowInfo->qosFlowIdentifier = msg->source2target->pdu_session_resource[i].qos_flow_info[q].qfi;
}
}
// Target Cell ID (NG-RAN CGI) (M)
source2target->targetCell_ID.present = NGAP_NGRAN_CGI_PR_nR_CGI;
asn1cCalloc(source2target->targetCell_ID.choice.nR_CGI, tNrCGI);
encode_ngap_nr_cgi(tNrCGI, &msg->target_gnb_id.plmn_identity, msg->source2target->targetCellId.nrCellIdentity);
// UE history Information (M)
asn1cSequenceAdd(source2target->uEHistoryInformation.list, NGAP_LastVisitedCellItem_t, lastVisitedCell);
lastVisitedCell->iE_Extensions = NULL;
// Last Visited Cell Information (M)
lastVisitedCell->lastVisitedCellInformation.present = NGAP_LastVisitedCellInformation_PR_nGRANCell;
// CHOICE (M): NG-RAN Cell
asn1cCalloc(lastVisitedCell->lastVisitedCellInformation.choice.nGRANCell, lastVisitedNR);
// Cell Type (M)
lastVisitedNR->cellType.cellSize = msg->source2target->ue_history_info.type;
// Global Cell ID (M)
lastVisitedNR->globalCellID.present = NGAP_NGRAN_CGI_PR_nR_CGI;
asn1cCalloc(lastVisitedNR->globalCellID.choice.nR_CGI, lastVisitedNrCGI);
cell_id_t *cell = &msg->source2target->ue_history_info.id;
encode_ngap_nr_cgi(lastVisitedNrCGI, &cell->plmn_identity, cell->nrCellIdentity);
// HO Cause Value (O)
if (msg->source2target->ue_history_info.cause) {
asn1cCalloc(lastVisitedNR->hOCauseValue, lastVisitedCause);
encode_ngap_cause(lastVisitedCause, msg->source2target->ue_history_info.cause);
}
// Time UE Stayed in Cell (M)
lastVisitedNR->timeUEStayedInCell = msg->source2target->ue_history_info.time_in_cell;
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
uint8_t source_to_target_transparent_container_buf[16384] = {0};
asn_enc_rval_t enc_rval = aper_encode_to_buffer(&asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer,
NULL,
(void *)source2target,
(void *)&source_to_target_transparent_container_buf,
16384);
ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
if (enc_rval.encoded < 0) {
AssertFatal(enc_rval.encoded > 0,
"HO LOG: Source to Transparent ASN1 message encoding failed (%s, %lu)!\n",
enc_rval.failed_type->name,
enc_rval.encoded);
return NULL;
}
int total_bytes = (enc_rval.encoded + 7) / 8;
int ret = OCTET_STRING_fromBuf(&ie->value.choice.SourceToTarget_TransparentContainer,
(const char *)&source_to_target_transparent_container_buf,
total_bytes);
if (ret != 0) {
LOG_E(NR_RRC, "HO LOG: Can not perform OCTET_STRING_fromBuf for the SourceToTarget_TransparentContainer");
return NULL;
}
return pdu;
}
NGAP_NGAP_PDU_t *encode_ng_handover_failure(const ngap_handover_failure_t *msg)
{
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
/* Prepare the NGAP message to encode */
pdu->present = NGAP_NGAP_PDU_PR_unsuccessfulOutcome;
asn1cCalloc(pdu->choice.unsuccessfulOutcome, head);
head->procedureCode = NGAP_ProcedureCode_id_HandoverResourceAllocation;
head->criticality = NGAP_Criticality_reject;
head->value.present = NGAP_UnsuccessfulOutcome__value_PR_HandoverFailure;
NGAP_HandoverFailure_t *out = &head->value.choice.HandoverFailure;
// AMF_UE_NGAP_ID (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverFailureIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
ie->criticality = NGAP_Criticality_reject;
ie->value.present = NGAP_HandoverFailureIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
}
// Cause (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverFailureIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_Cause;
ie->criticality = NGAP_Criticality_ignore;
ie->value.present = NGAP_HandoverFailureIEs__value_PR_Cause;
encode_ngap_cause(&ie->value.choice.Cause, &msg->cause);
}
return pdu;
}
static void free_ng_handover_request(ngap_handover_request_t *msg)
{
free_byte_array(msg->ue_ho_prep_info);
free_byte_array(msg->ue_cap);
free(msg->mobility_restriction);
}
int decode_ng_handover_request(ngap_handover_request_t *out, const NGAP_NGAP_PDU_t *pdu)
{
DevAssert(pdu != NULL);
NGAP_HandoverRequest_t *container = &pdu->choice.initiatingMessage->value.choice.HandoverRequest;
NGAP_HandoverRequestIEs_t *ie;
// Handover Type (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, true);
out->ho_type = ie->value.choice.HandoverType;
if (out->ho_type != HANDOVER_TYPE_INTRA5GS) {
NGAP_ERROR("Only Intra5GS Handover is supported at the moment!\n");
return -1;
}
// AMF UE NGAP ID (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &out->amf_ue_ngap_id);
// GUAMI (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_GUAMI, true);
out->guami = decode_ngap_guami(&ie->value.choice.GUAMI);
// UE Aggregate Maximum Bit Rate (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate, true);
out->ue_ambr = decode_ngap_UEAggregateMaximumBitRate(&ie->value.choice.UEAggregateMaximumBitRate);
// Allowed NSSAI (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AllowedNSSAI, true);
NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count);
out->nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count;
for (int i = 0; i < out->nb_allowed_nssais; i++) {
out->allowed_nssai[i] = decode_ngap_nssai(&ie->value.choice.AllowedNSSAI.list.array[i]->s_NSSAI);
}
// UE Security Capabilities (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UESecurityCapabilities, true);
out->security_capabilities = decode_ngap_security_capabilities(&ie->value.choice.UESecurityCapabilities);
// Security Context (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_SecurityContext, true);
NGAP_SecurityContext_t *sc = &ie->value.choice.SecurityContext;
memcpy(&out->security_context.next_hop, sc->nextHopNH.buf, sc->nextHopNH.size);
out->security_context.next_hop_chain_count = sc->nextHopChainingCount;
// Mobility Restriction List (O)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_MobilityRestrictionList, false);
if (ie != NULL) {
out->mobility_restriction = malloc_or_fail(sizeof(*out->mobility_restriction));
*out->mobility_restriction = decode_ngap_mobility_restriction(&ie->value.choice.MobilityRestrictionList);
}
// Cause (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_Cause, true);
out->cause = decode_ngap_cause(&ie->value.choice.Cause);
// Source to Target Transparent Container
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t,
ie,
container,
NGAP_ProtocolIE_ID_id_SourceToTarget_TransparentContainer,
true);
NGAP_SourceToTarget_TransparentContainer_t *choice = &ie->value.choice.SourceToTarget_TransparentContainer;
byte_array_t sourceToTargetTransparentContainer = create_byte_array(choice->size, choice->buf);
NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer_t *source2target = NULL;
asn_dec_rval_t dec_rval = aper_decode_complete(NULL,
&asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer,
(void **)&source2target,
sourceToTargetTransparentContainer.buf,
sourceToTargetTransparentContainer.len);
free_byte_array(sourceToTargetTransparentContainer);
if (dec_rval.code != RC_OK) {
free_ng_handover_request(out);
NGAP_ERROR("Failed to decode sourceToTargetTransparentContainer\n");
return -1;
}
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
// Extract Cell Identity
BIT_STRING_TO_NR_CELL_IDENTITY(&source2target->targetCell_ID.choice.nR_CGI->nRCellIdentity, out->nr_cell_id);
// Handover Preparation Information: store and decode
out->ue_ho_prep_info = create_byte_array(source2target->rRCContainer.size, source2target->rRCContainer.buf);
NR_HandoverPreparationInformation_t *hoPrepInformation = NULL;
asn_dec_rval_t hoPrep_dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_HandoverPreparationInformation,
(void **)&hoPrepInformation,
source2target->rRCContainer.buf,
source2target->rRCContainer.size);
AssertFatal(hoPrep_dec_rval.code == RC_OK && hoPrep_dec_rval.consumed > 0, "Handover Prep Info decode error\n");
if (hoPrep_dec_rval.code != RC_OK && !hoPrep_dec_rval.consumed) {
NGAP_ERROR("Failed to decode HandoverPreparationInformation, abort Handover Request decoding\n");
free_ng_handover_request(out);
ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
return -1;
}
ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, hoPrepInformation);
// Decode UE capabilities and store
NR_HandoverPreparationInformation_IEs_t *hpi =
hoPrepInformation->criticalExtensions.choice.c1->choice.handoverPreparationInformation;
const NR_UE_CapabilityRAT_ContainerList_t *ue_CapabilityRAT_ContainerList = &hpi->ue_CapabilityRAT_List;
out->ue_cap.len = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList,
NULL,
ue_CapabilityRAT_ContainerList,
(void **)&out->ue_cap.buf);
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hoPrepInformation);
if (out->ue_cap.len <= 0) {
free_ng_handover_request(out);
NGAP_ERROR("could not encode UE-CapabilityRAT-ContainerList\n");
return -1;
}
// PDU Session Resource Setup List (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t,
ie,
container,
NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListHOReq,
true);
out->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSetupListHOReq.list.count;
for (int pduSesIdx = 0; pduSesIdx < out->nb_of_pdusessions; ++pduSesIdx) {
NGAP_PDUSessionResourceSetupItemHOReq_t *item_p = ie->value.choice.PDUSessionResourceSetupListHOReq.list.array[pduSesIdx];
// PDU Session ID (M)
ho_request_pdusession_t *setup = &out->pduSessionResourceSetupList[pduSesIdx];
setup->pdusession_id = item_p->pDUSessionID;
setup->pdu_session_type = PDUSessionType_ipv4;
// S-NSSAI (M)
setup->nssai = decode_ngap_nssai(&item_p->s_NSSAI);
// Handover Request Transfer (M)
bool ret = decodePDUSessionResourceSetup(&setup->pdusessionTransfer, item_p->handoverRequestTransfer);
if (!ret) {
free_ng_handover_request(out);
NGAP_ERROR("Failed to decode pDUSessionResourceSetupRequestTransfer in NG Initial Context Setup Request\n");
return -1;
}
}
return 0;
}
NGAP_NGAP_PDU_t *encode_ng_handover_request_ack(ngap_handover_request_ack_t *msg)
{
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
pdu->present = NGAP_NGAP_PDU_PR_successfulOutcome;
asn1cCalloc(pdu->choice.successfulOutcome, head);
head->procedureCode = NGAP_ProcedureCode_id_HandoverResourceAllocation;
head->criticality = NGAP_Criticality_reject;
head->value.present = NGAP_SuccessfulOutcome__value_PR_HandoverRequestAcknowledge;
NGAP_HandoverRequestAcknowledge_t *out = &head->value.choice.HandoverRequestAcknowledge;
// AMF UE NGAP ID (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
ie->criticality = NGAP_Criticality_ignore;
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
}
// RAN UE NGAP ID (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
ie->criticality = NGAP_Criticality_ignore;
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_RAN_UE_NGAP_ID;
ie->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
}
// Target to Source Transparent Container (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_TargetToSource_TransparentContainer;
ie->criticality = NGAP_Criticality_reject;
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_TargetToSource_TransparentContainer;
NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer_t *t2s = calloc_or_fail(1, sizeof(*t2s));
// RRC Container (M)
{
// Encode Handover Command
t2s->rRCContainer.size = msg->target2source.len;
t2s->rRCContainer.buf = malloc_or_fail(msg->target2source.len);
memcpy(t2s->rRCContainer.buf, msg->target2source.buf, msg->target2source.len);
byte_array_t ba = { .buf = NULL, .len = 0 };
ba.len = aper_encode_to_new_buffer(&asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, NULL, t2s, (void **)&ba.buf);
OCTET_STRING_fromBuf(&ie->value.choice.TargetToSource_TransparentContainer, (const char *)ba.buf, ba.len);
ASN_STRUCT_FREE(asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, t2s);
free_byte_array(ba);
}
}
// PDU Session Resource Admitted List (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceAdmittedList;
ie->criticality = NGAP_Criticality_ignore;
ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_PDUSessionResourceAdmittedList;
for (int pduSesIdx = 0; pduSesIdx < msg->nb_of_pdusessions; pduSesIdx++) {
asn1cSequenceAdd(ie->value.choice.PDUSessionResourceAdmittedList.list, NGAP_PDUSessionResourceAdmittedItem_t, item);
item->pDUSessionID = msg->pdusessions[pduSesIdx].pdu_session_id;
/* dLQosFlowPerTNLInformation */
NGAP_HandoverRequestAcknowledgeTransfer_t transfer = {0};
transfer.dL_NGU_UP_TNLInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel;
asn1cCalloc(transfer.dL_NGU_UP_TNLInformation.choice.gTPTunnel, tmp);
GTP_TEID_TO_ASN1(msg->pdusessions[pduSesIdx].ack_transfer.gtp_teid, &tmp->gTP_TEID);
tnl_to_bitstring(&tmp->transportLayerAddress, msg->pdusessions[pduSesIdx].ack_transfer.gNB_addr);
for (int j = 0; j < msg->pdusessions[pduSesIdx].ack_transfer.nb_of_qos_flow; j++) {
asn1cSequenceAdd(transfer.qosFlowSetupResponseList.list, NGAP_QosFlowItemWithDataForwarding_t, qosItem);
qosItem->qosFlowIdentifier = msg->pdusessions[pduSesIdx].ack_transfer.qos_setup_list[j].qfi;
qosItem->dataForwardingAccepted = malloc_or_fail(sizeof(*qosItem->dataForwardingAccepted));
*qosItem->dataForwardingAccepted = NGAP_DataForwardingAccepted_data_forwarding_accepted;
}
void *buf;
ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_NGAP_HandoverRequestAcknowledgeTransfer, NULL, &transfer, &buf);
AssertFatal(encoded > 0, "ASN1 message encoding failed !\n");
item->handoverRequestAcknowledgeTransfer.buf = buf;
item->handoverRequestAcknowledgeTransfer.size = encoded;
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_HandoverRequestAcknowledgeTransfer, &transfer);
}
}
return pdu;
}
void free_ng_handover_req_ack(ngap_handover_request_ack_t *msg)
{
free_byte_array(msg->target2source);
}
/** @brief Decoder for the NG Handover Command */
int decode_ng_handover_command(ngap_handover_command_t *msg, NGAP_NGAP_PDU_t *pdu)
{
DevAssert(pdu != NULL);
NGAP_HandoverCommandIEs_t *ie;
NGAP_HandoverCommand_t *container = &pdu->choice.successfulOutcome->value.choice.HandoverCommand;
// AMF UE NGAP ID (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &msg->amf_ue_ngap_id);
// RAN UE NGAP ID (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
msg->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
// Handover Type (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, true);
if (ie->value.choice.HandoverType != HANDOVER_TYPE_INTRA5GS) {
NGAP_ERROR("Only Intra 5GS Handover is supported at the moment!\n");
return -1;
}
// PDU Session Resource Handover List (O)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, false);
if (ie != NULL) {
msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceHandoverList.list.count;
for (int i = 0; i < msg->nb_of_pdusessions; ++i) {
NGAP_PDUSessionResourceHandoverItem_t *item = ie->value.choice.PDUSessionResourceHandoverList.list.array[i];
// PDU Session ID (M)
msg->pdu_sessions[i].pdusession_id = item->pDUSessionID;
// Handover Command Transfer (M)
NGAP_HandoverCommandTransfer_t *hoCommandTransfer = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
&asn_DEF_NGAP_HandoverCommandTransfer,
(void **)&hoCommandTransfer,
item->handoverCommandTransfer.buf,
item->handoverCommandTransfer.size);
if (dec_rval.code != RC_OK) {
NGAP_ERROR("Failed to decode handoverCommandTransfer\n");
continue;
}
// DL Forwarding UP TNL Information (O)
if (hoCommandTransfer->dLForwardingUP_TNLInformation) {
NGAP_UPTransportLayerInformation_t *up_tnl = hoCommandTransfer->dLForwardingUP_TNLInformation;
if (hoCommandTransfer->dLForwardingUP_TNLInformation->present == NGAP_UPTransportLayerInformation_PR_gTPTunnel) {
OCTET_STRING_TO_INT32(&(up_tnl->choice.gTPTunnel->gTP_TEID), msg->pdu_sessions[i].ho_command_transfer.gtp_teid);
bitstring_to_tnl(&msg->pdu_sessions[i].ho_command_transfer.gNB_addr, up_tnl->choice.gTPTunnel->transportLayerAddress);
} else {
NGAP_WARN("Missing DL Forwarding UP TNL Information\n");
}
}
ASN_STRUCT_FREE(asn_DEF_NGAP_HandoverCommandTransfer, hoCommandTransfer);
}
}
// Target to Source Transparent Container (M)
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t,
ie,
container,
NGAP_ProtocolIE_ID_id_TargetToSource_TransparentContainer,
true);
NGAP_TargetToSource_TransparentContainer_t *target2source = &(ie->value.choice.TargetToSource_TransparentContainer);
NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer_t *transparentContainer = NULL;
asn_dec_rval_t dec_rval = aper_decode_complete(NULL,
&asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer,
(void **)&transparentContainer,
target2source->buf,
target2source->size);
if (dec_rval.code != RC_OK) {
NGAP_ERROR("Failed to decode TargetToSource_TransparentContainer\n");
return -1;
}
msg->handoverCommand = create_byte_array(transparentContainer->rRCContainer.size, transparentContainer->rRCContainer.buf);
ASN_STRUCT_FREE(asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, transparentContainer);
return 0;
}
void free_ng_handover_command(ngap_handover_command_t *msg)
{
free_byte_array(msg->handoverCommand);
}
NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg)
{
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
// Message Type (M)
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
asn1cCalloc(pdu->choice.initiatingMessage, head);
head->procedureCode = NGAP_ProcedureCode_id_HandoverNotification;
head->criticality = NGAP_Criticality_ignore;
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverNotify;
NGAP_HandoverNotify_t *out = &head->value.choice.HandoverNotify;
// AMF UE NGAP ID (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
ie->criticality = NGAP_Criticality_reject;
ie->value.present = NGAP_HandoverNotifyIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
}
// RAN UE NGAP ID (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
ie->criticality = NGAP_Criticality_reject;
ie->value.present = NGAP_HandoverNotifyIEs__value_PR_RAN_UE_NGAP_ID;
ie->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
}
// User Location Information (M)
{
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie);
ie->id = NGAP_ProtocolIE_ID_id_UserLocationInformation;
ie->criticality = NGAP_Criticality_ignore;
ie->value.present = NGAP_HandoverNotifyIEs__value_PR_UserLocationInformation;
ie->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR;
asn1cCalloc(ie->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p);
// NR User Location Information
const target_ran_node_id_t *target = &msg->user_info.target_ng_ran;
// CGI (M)
MACRO_GNB_ID_TO_CELL_IDENTITY(target->targetgNBId, msg->user_info.nrCellIdentity, &userinfo_nr_p->nR_CGI.nRCellIdentity);
MCC_MNC_TO_TBCD(target->plmn_identity.mcc,
target->plmn_identity.mnc,
target->plmn_identity.mnc_digit_length,
&userinfo_nr_p->nR_CGI.pLMNIdentity);
// TAI (M)
INT24_TO_OCTET_STRING(target->tac, &userinfo_nr_p->tAI.tAC);
MCC_MNC_TO_PLMNID(target->plmn_identity.mcc,
target->plmn_identity.mnc,
target->plmn_identity.mnc_digit_length,
&userinfo_nr_p->tAI.pLMNIdentity);
}
return pdu;
}
/** @brief NGAP Handover Cancel encoding (9.2.3.11 3GPP TS 38.413 Handover Cancel)
* 8.4.5 Handover Cancellation (source NG-RAN -> AMF) */
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg)
{
DevAssert(msg != NULL);
NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
asn1cCalloc(pdu->choice.initiatingMessage, head);
head->procedureCode = NGAP_ProcedureCode_id_HandoverCancel;
head->criticality = NGAP_Criticality_reject;
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverCancel;
NGAP_HandoverCancel_t *out = &head->value.choice.HandoverCancel;
// AMF UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie1);
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
ie1->criticality = NGAP_Criticality_reject;
ie1->value.present = NGAP_HandoverCancelIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
// RAN UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie2);
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
ie2->criticality = NGAP_Criticality_reject;
ie2->value.present = NGAP_HandoverCancelIEs__value_PR_RAN_UE_NGAP_ID;
ie2->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
// Cause (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie3);
ie3->id = NGAP_ProtocolIE_ID_id_Cause;
ie3->criticality = NGAP_Criticality_reject;
ie3->value.present = NGAP_HandoverCancelIEs__value_PR_Cause;
encode_ngap_cause(&ie3->value.choice.Cause, &msg->cause);
return pdu;
}
/** @brief Decode NGAP Handover Cancel Acknowledge (9.2.3.12 3GPP TS 38.413) */
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu)
{
DevAssert(out != NULL);
DevAssert(pdu != NULL);
NGAP_HandoverCancelAcknowledge_t *container = &pdu->choice.successfulOutcome->value.choice.HandoverCancelAcknowledge;
NGAP_HandoverCancelAcknowledgeIEs_t *ie;
/* AMF UE NGAP ID (M) */
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCancelAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
asn_INTEGER2ulong(&ie->value.choice.AMF_UE_NGAP_ID, &out->amf_ue_ngap_id);
/* RAN UE NGAP ID (M) */
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCancelAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
out->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
return 0;
}
/** @brief Encode NGAP UL RAN Status Transfer (9.2.3.14 of 3GPP TS 38.413) */
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg)
{
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
asn1cCalloc(pdu->choice.initiatingMessage, head);
head->procedureCode = NGAP_ProcedureCode_id_UplinkRANStatusTransfer;
head->criticality = NGAP_Criticality_ignore;
head->value.present = NGAP_InitiatingMessage__value_PR_UplinkRANStatusTransfer;
NGAP_UplinkRANStatusTransfer_t *out = &head->value.choice.UplinkRANStatusTransfer;
// AMF UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_UplinkRANStatusTransferIEs_t, ie1);
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
ie1->criticality = NGAP_Criticality_reject;
ie1->value.present = NGAP_UplinkRANStatusTransferIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
// RAN UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_UplinkRANStatusTransferIEs_t, ie2);
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
ie2->criticality = NGAP_Criticality_reject;
ie2->value.present = NGAP_UplinkRANStatusTransferIEs__value_PR_RAN_UE_NGAP_ID;
ie2->value.choice.RAN_UE_NGAP_ID = msg->gnb_ue_ngap_id;
// RAN Status Transfer Transparent Container (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_UplinkRANStatusTransferIEs_t, ie3);
ie3->id = NGAP_ProtocolIE_ID_id_RANStatusTransfer_TransparentContainer;
ie3->criticality = NGAP_Criticality_reject;
ie3->value.present = NGAP_UplinkRANStatusTransferIEs__value_PR_RANStatusTransfer_TransparentContainer;
NGAP_RANStatusTransfer_TransparentContainer_t *container = &ie3->value.choice.RANStatusTransfer_TransparentContainer;
for (int i = 0; i < msg->ran_status.nb_drb; ++i) {
const ngap_drb_status_t *s = &msg->ran_status.drb_status_list[i];
asn1cSequenceAdd(container->dRBsSubjectToStatusTransferList.list, NGAP_DRBsSubjectToStatusTransferItem_t, drb_item);
drb_item->dRB_ID = s->drb_id;
// UL COUNT
if (s->ul_count.sn_len == NGAP_SN_LENGTH_18) {
drb_item->dRBStatusUL.present = NGAP_DRBStatusUL_PR_dRBStatusUL18;
asn1cCalloc(drb_item->dRBStatusUL.choice.dRBStatusUL18, ul18);
ul18->uL_COUNTValue.pDCP_SN18 = s->ul_count.pdcp_sn;
ul18->uL_COUNTValue.hFN_PDCP_SN18 = s->ul_count.hfn;
} else {
drb_item->dRBStatusUL.present = NGAP_DRBStatusUL_PR_dRBStatusUL12;
asn1cCalloc(drb_item->dRBStatusUL.choice.dRBStatusUL12, ul12);
ul12->uL_COUNTValue.pDCP_SN12 = s->ul_count.pdcp_sn;
ul12->uL_COUNTValue.hFN_PDCP_SN12 = s->ul_count.hfn;
}
// DL COUNT
if (s->dl_count.sn_len == NGAP_SN_LENGTH_18) {
drb_item->dRBStatusDL.present = NGAP_DRBStatusDL_PR_dRBStatusDL18;
asn1cCalloc(drb_item->dRBStatusDL.choice.dRBStatusDL18, dl18);
dl18->dL_COUNTValue.pDCP_SN18 = s->dl_count.pdcp_sn;
dl18->dL_COUNTValue.hFN_PDCP_SN18 = s->dl_count.hfn;
} else {
drb_item->dRBStatusDL.present = NGAP_DRBStatusDL_PR_dRBStatusDL12;
asn1cCalloc(drb_item->dRBStatusDL.choice.dRBStatusDL12, dl12);
dl12->dL_COUNTValue.pDCP_SN12 = s->dl_count.pdcp_sn;
dl12->dL_COUNTValue.hFN_PDCP_SN12 = s->dl_count.hfn;
}
}
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NGAP_RANStatusTransfer_TransparentContainer, &container);
return pdu;
}
@@ -0,0 +1,41 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef NGAP_GNB_MOBILITY_MANAGEMENT_H_
#define NGAP_GNB_MOBILITY_MANAGEMENT_H_
#include <stdint.h>
#include "common/platform_types.h"
#include "ngap_messages_types.h"
NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg);
NGAP_NGAP_PDU_t *encode_ng_handover_failure(const ngap_handover_failure_t *msg);
int decode_ng_handover_request(ngap_handover_request_t *out, const NGAP_NGAP_PDU_t *pdu);
NGAP_NGAP_PDU_t *encode_ng_handover_request_ack(ngap_handover_request_ack_t *msg);
void free_ng_handover_req_ack(ngap_handover_request_ack_t *msg);
int decode_ng_handover_command(ngap_handover_command_t *msg, NGAP_NGAP_PDU_t *pdu);
void free_ng_handover_command(ngap_handover_command_t *msg);
NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg);
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg);
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg);
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu);
#endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */
+6
View File
@@ -76,6 +76,12 @@ struct ngap_gNB_ue_context_s *ngap_get_ue_context(uint32_t gNB_ue_ngap_id)
return RB_FIND(ngap_ue_map, &ngap_ue_head, &temp);
}
ngap_gNB_ue_context_t *ngap_get_ue_context_from_amf_ue_ngap_id(uint32_t amf_ue_ngap_id)
{
ngap_gNB_ue_context_t temp = {.amf_ue_ngap_id = amf_ue_ngap_id};
return RB_FIND(ngap_ue_map, &ngap_ue_head, &temp);
}
struct ngap_gNB_ue_context_s *ngap_detach_ue_context(uint32_t gNB_ue_ngap_id)
{
struct ngap_gNB_ue_context_s *tmp = ngap_get_ue_context(gNB_ue_ngap_id);
+1
View File
@@ -81,6 +81,7 @@ typedef struct ngap_gNB_ue_context_s {
void ngap_store_ue_context(const ngap_gNB_ue_context_t *ue_desc_p);
ngap_gNB_ue_context_t *ngap_get_ue_context(uint32_t gNB_ue_ngap_id);
ngap_gNB_ue_context_t *ngap_get_ue_context_from_amf_ue_ngap_id(uint32_t amf_ue_ngap_id);
ngap_gNB_ue_context_t *ngap_detach_ue_context(uint32_t gNB_ue_ngap_id);
#endif /* NGAP_GNB_UE_CONTEXT_H_ */
+31
View File
@@ -78,5 +78,36 @@
#include "NGAP_Dynamic5QIDescriptor.h"
#include "NGAP_PDUSessionResourceModifyRequestTransfer.h"
#include "NGAP_QosFlowAddOrModifyRequestItem.h"
#include "NGAP_PDUSessionResourceItemHORqd.h"
#include "NGAP_PDUSessionResourceInformationItem.h"
#include "NGAP_QosFlowInformationItem.h"
#include "NGAP_LastVisitedCellItem.h"
#include "NGAP_LastVisitedNGRANCellInformation.h"
#include "NGAP_PDUSessionResourceInformationList.h"
#include "NGAP_HandoverRequiredTransfer.h"
#include "NGAP_SourceNGRANNode-ToTargetNGRANNode-TransparentContainer.h"
#include "NGAP_PDUSessionResourceSetupItemHOReq.h"
#include "NGAP_HandoverCommand.h"
#include "NGAP_HandoverCommandTransfer.h"
#include "NGAP_TargetNGRANNode-ToSourceNGRANNode-TransparentContainer.h"
#include "NGAP_TargetToSource-TransparentContainer.h"
#include "NGAP_UEHistoryInformation.h"
#include "NGAP_Cause.h"
#include "NGAP_PDUSessionResourceHandoverItem.h"
#include "NGAP_SourceToTarget-TransparentContainer.h"
#include "NGAP_HandoverRequest.h"
#include "NGAP_PDUSessionResourceAdmittedList.h"
#include "NGAP_PDUSessionResourceAdmittedItem.h"
#include "NGAP_HandoverRequestAcknowledgeTransfer.h"
#include "NGAP_QosFlowItemWithDataForwarding.h"
#include "NR_HandoverPreparationInformation.h"
#include "NGAP_HandoverNotify.h"
#include "NGAP_DRBsSubjectToStatusTransferItem.h"
#include "NGAP_DRBStatusUL.h"
#include "NGAP_DRBStatusUL18.h"
#include "NGAP_DRBStatusUL12.h"
#include "NGAP_DRBStatusDL.h"
#include "NGAP_DRBStatusDL18.h"
#include "NGAP_DRBStatusDL12.h"
#endif // NGAP_MSG_INCLUDES_H
@@ -0,0 +1,251 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
@include "neighbour-config-rfsim.conf"
nr_cellid = 12345678L;
////////// Physical parameters:
do_CSIRS = 1;
do_SRS = 1;
#uess_agg_levels = [0,1,2,2,1]
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 641280;
dl_frequencyBand = 78;
# this is 3600 MHz
dl_absoluteFrequencyPointA = 640008;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=27,L=48 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 13;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.70.132"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 150;
pucch_TargetSNRx10 = 200;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
pucch0_dtx_threshold = 100;
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 1
nb_rx = 1
att_tx = 12;
att_rx = 12;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 114;
eNB_instances = [0];
clock_src = "internal";
}
);
rfsimulator :
{
serveraddr = "server";
serverport = 4043;
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
};
e2_agent = {
near_ric_ip_addr = "127.0.0.1";
#sm_dir = "/path/where/the/SMs/are/located/"
sm_dir = "/usr/local/lib/flexric/"
};
@@ -0,0 +1,251 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xb00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
@include "neighbour-config-rfsim.conf"
nr_cellid = 720898;
////////// Physical parameters:
do_CSIRS = 1;
do_SRS = 1;
#uess_agg_levels = [0,1,2,2,1]
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 1;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 621312;
dl_frequencyBand = 78;
# this is 3600 MHz
dl_absoluteFrequencyPointA = 620040;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=27,L=48 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 13;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.70.132"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 150;
pucch_TargetSNRx10 = 200;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
pucch0_dtx_threshold = 100;
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 1
nb_rx = 1
att_tx = 12;
att_rx = 12;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 114;
eNB_instances = [0];
clock_src = "internal";
}
);
rfsimulator :
{
serveraddr = "server";
serverport = 4043;
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
};
e2_agent = {
near_ric_ip_addr = "127.0.0.1";
#sm_dir = "/path/where/the/SMs/are/located/"
sm_dir = "/usr/local/lib/flexric/"
};
@@ -0,0 +1,78 @@
############################################################
# gNB-to-gNB neighbour list + measurement configuration #
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
############################################################
neighbour_list = (
##########################################################
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 12345678L) #
##########################################################
{
nr_cellid = 12345678L; # Serving cell of gNB 0xe00
neighbour_cell_configuration = (
{
gNB_ID = 0xb00;
nr_cellid = 720898; # Cell served by gNB 0xb00
physical_cellId = 1;
absoluteFrequencySSB= 621312;
subcarrierSpacing = 1; # 30 kHz
band = 78;
plmn = { mcc = 001; mnc = 01; mnc_length = 2 };
tracking_area_code = 1;
}
);
},
##########################################################
# Entry USED BY gNB_ID = 0xb00 (nr_cellid = 720898) #
##########################################################
{
nr_cellid = 720898; # Serving cell of gNB 0xb00
neighbour_cell_configuration = (
{
gNB_ID = 0xe00;
nr_cellid = 12345678L; # Cell served by gNB 0xe00
physical_cellId = 0;
absoluteFrequencySSB= 641280;
subcarrierSpacing = 1; # 30 kHz
band = 78;
plmn = { mcc = 001; mnc = 01; mnc_length = 2 };
tracking_area_code = 1;
}
);
}
);
############################################################
# Common NR measurement-event configuration #
############################################################
nr_measurement_configuration = {
Periodical = {
enable = 1;
includeBeamMeasurements = 1;
maxNrofRS_IndexesToReport = 4;
};
A2 = {
enable = 1;
threshold = 60;
timeToTrigger = 1;
};
A3 = (
{
cell_id = 720898; # neighbour of gNB 0xe00
offset = 10;
hysteresis = 0;
timeToTrigger = 1;
},
{
cell_id = 12345678; # neighbour of gNB 0xb00
offset = 5;
hysteresis = 1;
timeToTrigger = 2;
}
);
};