Merge remote-tracking branch 'origin/issue-837' into integration_2026_w05 (!3732)
Implement TS 38.473 transparency for CellGroupConfig forwarding and handle re-establishment on a different DU
This MR implements the following fixes that are relevant in RRC
procedures (e.g. re-establishment, reconfiguration):
1. Transparent CellGroupConfig forwarding
This MR implements TS 38.473 transparency requirements by ensuring that
the CellGroupConfig IE is transparently forwarded from the gNB-DU to the
UE without any re-encoding at the gNB-CU. The CU stores and forwards the
encoded bytes transparently, while the DU is responsible for all
encoding and modifications (e.g., reestablishRLC flags). Motivation
Motivation: The 3GPP TS 38.473 specification mandates that the
CellGroupConfig IE shall be transparently signaled to the UE.
Previously, the CU was re-encoding CellGroupConfig depending on the
scenario, instead of retrieving it from the DU, violating the
transparency requirement. CU may decode for internal use but must not
re-encode for forwarding. Solution
Solution: Core Principle: The CU stores the encoded CellGroupConfig
bytes received from the DU and forwards them directly to the UE without
any re-encoding. The DU handles all encoding and modifications.
Key Changes:
1. Transparent Forwarding in RRC Reconfiguration
- Modified build_RRCReconfiguration_IEs() to use pre-encoded bytes
directly
- Changed parameter from cell_group_config to cgc (encoded byte
array with buffer and size)
2. Encoded Bytes Storage
- Added byte_array_t mcg field to RRC UE structure to store encoded
CellGroupConfig
- Added store_cgc() helper function to save encoded cell group in
the RRC UE Context at gNB (from F1AP messages)
3. DU Encoding Responsibilities
- Add gNB-DU Configuration Query IE support to trigger cgc encoding
to request CellGroupConfig from DU
- DU encodes CellGroupConfig with proper reestablishRLC flags
during re-establishment
- DU removes SRB1 from rlc-BearerToAddModList during
re-establishment (SRB1 re-establishment is done before
RRCReconfiguration)
4. New functions
- Add rrc_detect_reestablishment() function to detect
re-establishment during UE Context Modification
- Extracted rrc_gNB_first_reconfiguration_after_reestablishment()
function to handle first reconfiguration after re-establishment
- Added dump_cgc() and dump_mcg() utility functions for debugging
purposes
5. Re-establishment Flow
- rrc_gNB_process_RRCReestablishmentComplete() requests
CellGroupConfig from DU via gNB_DU_Configuration_Query in UE
Context Modification Request
- DU detects re-establishment and add reestablishRLC IEs
- CU stores encoded CGC from the UE Context Modification Response
and forwards it transparently in subsequent RRC Reconfiguration
Testing
Manual Testing:
- RRC re-establishment procedure completes successfully
- RRC Reconfiguration after re-establishment includes all DU-provided information
Issues fixed
Closes issue #837
2. Trigger UE Context Setup fallback upon reestablishment on DU
different from the original one
As per 38.401 §8.7: if the UE accesses a DU different from the original
one during re-establishment, the CU must trigger UE Context Setup toward
the new DU. This refers to steps 9-10 and steps 9’-10’. Either one or
the other can be executed during re-establishment.
e.g. if this happens during handover, it shall trigger a UE Context
Setup fallback on the CU.
This MR implements the required behavior by:
- Adding a new helper function rrc_handle_f1_ho_reestab_on_target() to
handle F1 handover reestablishment on target DU. The function triggers
UE Context Setup procedure when the UE reestablishes on the target DU
during an ongoing F1 handover.
- Calling this function when detecting that the UE is reestablishing on
the target DU during handover, i.e.: (1) from
handle_rrcReestablishmentComplete(), as per 38.401 §8.7, NOTE on steps
9-10: CU gets UL RRC MESSAGE TRANSFER from a DU different than the
original (2) from rrc_CU_process_ue_modification_required, as per
38.401 §8.7, NOTE on steps 9'-10': CU gets a UE CONTEXT MODIFICATION
REQUIRED from a DU different from the original one.
Also:
- add helper rrc_fill_f1_ue_context_setup and adopt it in handover
- move DRB list filler fill_drb_to_be_setup into rrc_gNB.c and rename it
rrc_fill_f1_drb_to_setup for consistency
See also TS 38.401 Figure 8.7-1 "RRC connection reestablishment
procedure" for reference
3. DU: Handle missing old UE context in DL RRC Message Transfer
During F1 handover reestablishment, the CU may forward a DL RRC Message
Transfer to the target DU with old_gNB_DU_ue_id included, even though
the target DU does not have the old UE context yet.
The code previously used AssertFatal when the old UE context was not
found, causing a crash: "CU claims we should know UE %04x, but we don't"
According to TS 38.473, the gNB-DU shall release the old gNB-DU UE F1AP ID only:
> if the gNB-DU identifies the UE-associated logical F1-connection If
> there is no matching connection, there is nothing to release.
Fix:
- Check if old UE context exists before accessing it
- If old UE does not exist, log error, clean up any stale F1 UE data,
and return early (do not deliver RRC message since reestablishment
can't be completed)
The goal is to address a recurrent issue in the F1 pipeline, see
jenkins-oai.eurecom.fr/job/RAN-SA-Handover-CN5G/514:
Assertion (oldUE) failed!
In dl_rrc_message_transfer() /oai-ran/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c:956
CU claims we should know UE 35ce, but we don't
This commit is contained in:
@@ -47,8 +47,8 @@ nr_measurement_configuration = {
|
||||
|
||||
A3 = ({
|
||||
physCellId = -1; #Default
|
||||
offset = 10;
|
||||
hysteresis = 0;
|
||||
time_to_trigger = 1
|
||||
offset = 16;
|
||||
hysteresis = 1;
|
||||
time_to_trigger = 10;
|
||||
})
|
||||
};
|
||||
|
||||
@@ -240,7 +240,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 1?</desc>
|
||||
<node>localhost</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3585"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 192.168.71.150 9090 | grep "3585"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -271,7 +271,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 0?</desc>
|
||||
<node>localhost</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3584"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 192.168.71.150 9090 | grep "3584"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -302,7 +302,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 1?</desc>
|
||||
<node>localhost</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3585"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 192.168.71.150 9090 | grep "3585"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 1?</desc>
|
||||
<node>localhost</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.180 9090 | grep "1234"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 192.168.71.180 9090 | grep "1234"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -181,7 +181,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 0?</desc>
|
||||
<node>localhost</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.150 9090 | grep "3584"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 192.168.71.150 9090 | grep "3584"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -218,7 +218,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE (1) connected to DU-PCI 1?</desc>
|
||||
<node>localhost</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 192.168.71.180 9090 | grep "1234"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 192.168.71.180 9090 | grep "1234"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
|
||||
@@ -59,9 +59,9 @@
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Set attenuator to 0 for DU0 and 17 for DU1</desc>
|
||||
<desc>Set attenuator to 0 for DU0 and 22 for DU1</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 25</command>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 22</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -76,8 +76,8 @@
|
||||
<class>Deploy_Object</class>
|
||||
<desc>Deploy gNB-DU0 (TDD/Band77/20MHz/B210) in a container</desc>
|
||||
<yaml_path>ci-scripts/yaml_files/5g_sa_f1_b210_ho</yaml_path>
|
||||
<services>oai-du0</services>
|
||||
<node>groot</node>
|
||||
<services>oai-du0</services>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -94,26 +94,11 @@
|
||||
<id>raspix</id>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Ping</class>
|
||||
<desc>Ping: 100pings in 20sec</desc>
|
||||
<id>raspix</id>
|
||||
<svr_id>oc-cn5g-00103-ho</svr_id>
|
||||
<ping_args>-c 100 -i 0.2</ping_args>
|
||||
<ping_packetloss_threshold>5</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>IdleSleep</class>
|
||||
<desc>Sleep</desc>
|
||||
<idle_sleep_time_in_sec>5</idle_sleep_time_in_sec>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Attenuate DU0 by 15 and remove the attenuation on DU1 to trigger Handover</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 15 -r 3,4 0 -d 5</command>
|
||||
<desc>UE connected to DU0?</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3584"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -122,21 +107,58 @@
|
||||
<id>raspix</id>
|
||||
<svr_id>oc-cn5g-00103-ho</svr_id>
|
||||
<ping_args>-c 50 -i 0.2</ping_args>
|
||||
<ping_packetloss_threshold>5</ping_packetloss_threshold>
|
||||
<ping_packetloss_threshold>30</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Set attenuation 5 on DU 0 and remove attenuation on DU 1 to test Reestablishment on a different DU (DU 1)</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 5 -r 3,4 0 -d 5</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Trigger Reestablishment (on DU 0)</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci force_reestab | ncat 127.0.0.1 9091 | grep -E 'Reset RLC counters of UE RNTI [0-9a-f]{4} to trigger reestablishment'</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Ping</class>
|
||||
<desc>Ping: 50pings in 10sec</desc>
|
||||
<id>raspix</id>
|
||||
<svr_id>oc-cn5g-00103-ho</svr_id>
|
||||
<ping_args>-c 50 -i 0.2</ping_args>
|
||||
<ping_packetloss_threshold>30</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE connected to DU1?</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 127.0.0.1 9090 | grep "3585"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3585"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Attenuate DU1 by 17dB and remove attenuation on DU0 to trigger Handover</desc>
|
||||
<desc>Verify Reestablishment (on CU)</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci get_reestab_count | ncat 127.0.0.1 9090 | grep -E 'UE RNTI [0-9a-f]{4} reestab 1'</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Remove attenuation on DU 0 and set attenuation on DU 1 to 5 to test Reestablishment on a different DU (DU 0)</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 25 -d 5</command>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 5 -d 5</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Trigger Reestablishment (on DU 1)</desc>
|
||||
<node>rocket</node>
|
||||
<command>echo ci force_reestab | ncat 127.0.0.1 9091 | grep -E 'Reset RLC counters of UE RNTI [0-9a-f]{4} to trigger reestablishment'</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -145,21 +167,28 @@
|
||||
<id>raspix</id>
|
||||
<svr_id>oc-cn5g-00103-ho</svr_id>
|
||||
<ping_args>-c 50 -i 0.2</ping_args>
|
||||
<ping_packetloss_threshold>5</ping_packetloss_threshold>
|
||||
<ping_packetloss_threshold>30</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE connected to DU0?</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 127.0.0.1 9090 | grep "3584"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3584"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Attenuate DU0 by 15 and remove the attenuation on DU1 to trigger Handover</desc>
|
||||
<desc>Verify Reestablishment (on CU)</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci get_reestab_count | ncat 127.0.0.1 9090 | grep -E 'UE RNTI [0-9a-f]{4} reestab 2'</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Attenuate DU0 by 22 and remove the attenuation on DU1 to trigger Handover</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 15 -r 3,4 0 -d 5</command>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 22 -r 3,4 0 -d 5</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -168,14 +197,60 @@
|
||||
<id>raspix</id>
|
||||
<svr_id>oc-cn5g-00103-ho</svr_id>
|
||||
<ping_args>-c 50 -i 0.2</ping_args>
|
||||
<ping_packetloss_threshold>5</ping_packetloss_threshold>
|
||||
<ping_packetloss_threshold>30</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE connected to DU1?</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 127.0.0.1 9090 | grep "3585"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3585"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Attenuate DU1 by 22dB and remove attenuation on DU0 to trigger Handover</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 22 -d 5</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Ping</class>
|
||||
<desc>Ping: 50pings in 10sec</desc>
|
||||
<id>raspix</id>
|
||||
<svr_id>oc-cn5g-00103-ho</svr_id>
|
||||
<ping_args>-c 50 -i 0.2</ping_args>
|
||||
<ping_packetloss_threshold>30</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE connected to DU0?</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3584"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Attenuate DU0 by 22 and remove the attenuation on DU1 to trigger Handover</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 22 -r 3,4 0 -d 5</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Ping</class>
|
||||
<desc>Ping: 50pings in 10sec</desc>
|
||||
<id>raspix</id>
|
||||
<svr_id>oc-cn5g-00103-ho</svr_id>
|
||||
<ping_args>-c 50 -i 0.2</ping_args>
|
||||
<ping_packetloss_threshold>30</ping_packetloss_threshold>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE connected to DU1?</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3585"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>Set initial attenuation on target gNB</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 25</command>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -s 1,2 0 -s 3,4 22</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -103,9 +103,9 @@
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Gradually attenuate gNB-PCI0 (ch1-2=15dB) and gNB-PCI1 (ch3-4=0dB) over 5s to trigger N2 Handover to gNB-PCI1</desc>
|
||||
<desc>Gradually attenuate gNB-PCI0 (ch1-2=22dB) and gNB-PCI1 (ch3-4=0dB) over 5s to trigger N2 Handover to gNB-PCI1</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 15 -r 3,4 0 -d 5</command>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 22 -r 3,4 0 -d 5</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -121,14 +121,14 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE connected to gNB-PCI1?</desc>
|
||||
<node>rocket</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 127.0.0.1 9090 | grep "3585"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3585"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
<class>Custom_Command</class>
|
||||
<desc>Gradually attenuate gNB-PCI0 (ch1-2=0dB) and gNB-PCI1 (ch3-4=15dB) over 5s to trigger N2 Handover back to gNB-PCI0</desc>
|
||||
<desc>Gradually attenuate gNB-PCI0 (ch1-2=0dB) and gNB-PCI1 (ch3-4=22dB) over 5s to trigger N2 Handover back to gNB-PCI0</desc>
|
||||
<node>rocket</node>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 25 -d 5</command>
|
||||
<command>python3 %%workspace%%/ci-scripts/attenuatorctl.py -r 1,2 0 -r 3,4 22 -d 5</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
@@ -144,7 +144,7 @@
|
||||
<class>Custom_Command</class>
|
||||
<desc>UE connected to gNB-PCI0?</desc>
|
||||
<node>groot</node>
|
||||
<command>echo ci fetch_du_by_ue_id 1 | ncat 127.0.0.1 9090 | grep "3584"</command>
|
||||
<command>echo ci fetch_du_by_ue_id | ncat 127.0.0.1 9090 | grep "3584"</command>
|
||||
</testCase>
|
||||
|
||||
<testCase>
|
||||
|
||||
@@ -154,10 +154,17 @@ int fetch_du_by_ue_id(char *buf, int debug, telnet_printfunc_t prnt)
|
||||
if (!RC.nrrrc)
|
||||
ERROR_MSG_RET("no RRC present, cannot list counts\n");
|
||||
|
||||
ue_id_t ue_id = 1;
|
||||
ue_id_t ue_id;
|
||||
if (buf) {
|
||||
ue_id = strtol(buf, NULL, 10);
|
||||
} else {
|
||||
// No UE ID provided: find the connected UE first
|
||||
rrc_gNB_ue_context_t *ue = get_single_rrc_ue();
|
||||
if (!ue)
|
||||
ERROR_MSG_RET("no single UE in RRC present\n");
|
||||
ue_id = ue->ue_context.rrc_ue_id;
|
||||
}
|
||||
|
||||
nr_rrc_du_container_t *du = get_du_for_ue(RC.nrrrc[0], ue_id);
|
||||
|
||||
if (du) {
|
||||
|
||||
+45
-10
@@ -175,6 +175,12 @@ with the reestablishment procedure; if any check fails, the CU-CP tries to
|
||||
release the old UE at the AMF, and continues with the "normal" connection setup
|
||||
(registration/service request) described above.
|
||||
|
||||
The re-establishment flow implements transparent forwarding of CellGroupConfig
|
||||
per TS 38.473: the CU requests CellGroupConfig from the DU via UE Context
|
||||
Modification Request with `gNB_DU_Configuration_Query=true`, and the DU responds
|
||||
with an encoded CellGroupConfig that the CU forwards to the UE without
|
||||
decode/re-encode cycles.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant ue as UE
|
||||
@@ -185,24 +191,45 @@ sequenceDiagram
|
||||
|
||||
ue->>du: Msg1/Preamble
|
||||
du->>ue: Msg2/RAR
|
||||
ue->>du: Msg3 w/ RRC Reestabishment Request (old C-RNTI/PCI)
|
||||
ue->>du: Msg3 w/ RRC Reestablishment Request (old C-RNTI/PCI)
|
||||
Note over du: DU creates UE context for new RNTI
|
||||
du->>cucp: F1AP Initial UL RRC Msg Tr (RRC Reestab Req, in Msg3)
|
||||
Note over cucp: rrc_handle_RRCReestablishmentRequest()
|
||||
alt UE known and at the original DU
|
||||
Note over cucp: rrc_gNB_generate_RRCReestablishment()<br />SRB1 PDCP reestablishment
|
||||
cucp->>du: F1AP DL RRC Msg Tr (old gNB-DU-UE ID)
|
||||
Note over du: Reuse configuration of and delete old UE<br />(in dl_rrc_message_transfer())
|
||||
alt UE known
|
||||
opt PCI mismatch AND Association ID mismatch
|
||||
Note over cucp: Detect different DU (assoc_id != du_assoc_id)<br/>Update du_assoc_id, set f1_ue_context_active=false
|
||||
end
|
||||
Note over cucp: rrc_gNB_generate_RRCReestablishment()<br/>SRB1 PDCP reestablishment
|
||||
cucp->>du: F1AP DL RRC Msg Tr (gNB-DU-UE ID)
|
||||
Note over du: dl_rrc_message_transfer()
|
||||
alt Old UE found
|
||||
Note over du: Reuse configuration of and delete old UE
|
||||
else Old UE not found
|
||||
Note over du: do nothing
|
||||
end
|
||||
Note over du: Set reestablishRLC=true
|
||||
Note over du: - Store CellGroup in reconfigCellGroup<br/>- Remove spCellConfig from CellGroup
|
||||
du->>ue: RRC Reestablishment
|
||||
ue->>du: RRC Reestablishment Complete
|
||||
du->>cucp: F1AP UL RRC Msg Tr (RRC Reestab Complete)
|
||||
Note over cucp: handle_rrcReestablishmentComplete()
|
||||
Note over cucp: cuup_notify_reestablishment()
|
||||
cucp->>cuup: E1AP Bearer Context Modification Req
|
||||
cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration)
|
||||
cuup->>cucp: E1AP Bearer Context Modification Resp
|
||||
Note over cucp: rrc_gNB_process_RRCReestablishmentComplete()<br/>Re-establish SRB2 PDCP
|
||||
Note over cucp: cuup_notify_reestablishment()<br/>Notify CU-UP for DRB re-establishment
|
||||
cucp->>cuup: E1AP Bearer Context Modification Req
|
||||
cuup->>cucp: E1AP Bearer Context Modification Resp (asynchronous)
|
||||
alt on the original DU
|
||||
Note over cucp: Send UE Context Modification Request<br/>with gNB_DU_Configuration_Query=true
|
||||
cucp->>du: F1AP UE Context Modification Req
|
||||
du->>cucp: F1AP UE Context Modification Resp<br/>(CellGroupConfig with spCellConfig and reestablishRLC flags)
|
||||
else on a different DU
|
||||
Note over cucp: Detect different DU (!f1_ue_context_active)<br/>Trigger UE Context Setup on new DU
|
||||
cucp->>du: F1AP UE Context Setup Request<br/>
|
||||
du->>cucp: F1AP UE Context Setup Response<br/>(CellGroupConfig with spCellConfig and reestablishRLC flags)
|
||||
end
|
||||
Note over cucp: rrc_CU_process_ue_context_setup/modification_response()<br/>Detect re-establishment via rrc_detect_reestablishment()<br/>rrc_gNB_generate_dedicatedRRCReconfiguration()<br/>(with is_reestablishment=true)
|
||||
cucp->>ue: F1AP DL RRC Msg Transfer (RRC Reconfiguration<br/>with transparent CellGroupConfig)
|
||||
ue->>cucp: F1AP UL RRC Msg Transfer (RRC Reconfiguration Complete)
|
||||
Note over cucp: handle_rrcReestablishmentComplete()
|
||||
Note over cucp: handle_rrcReconfigurationComplete()
|
||||
else Fallback*
|
||||
cucp->>amf: NGAP UE Context Release Request
|
||||
Note over cucp: rrc_gNB_generate_RRCSetup()
|
||||
@@ -210,6 +237,14 @@ sequenceDiagram
|
||||
end
|
||||
```
|
||||
|
||||
Note on re-establishment on different DU: The "UE known and on a different DU"
|
||||
case is shown in the sequence diagram above. When a UE re-establishes on a
|
||||
different DU than the original one (detected by comparing `du_assoc_id` at
|
||||
RRCReestablishmentRequest), the CU updates `du_assoc_id` and sets
|
||||
`f1_ue_context_active=false`. At RRCReestablishmentComplete, if
|
||||
`f1_ue_context_active` is false, the CU triggers UE Context Setup on the new
|
||||
DU per TS 38.401 §8.7.
|
||||
|
||||
### Inter-DU Handover (F1)
|
||||
|
||||
The basic handover (HO) structure is as follows. In order to support various
|
||||
|
||||
@@ -550,6 +550,9 @@ typedef struct f1ap_ue_context_mod_req_t {
|
||||
|
||||
byte_array_t *rrc_container;
|
||||
|
||||
// gNB-DU Configuration Query
|
||||
bool *gNB_DU_Configuration_Query;
|
||||
|
||||
int srbs_len;
|
||||
f1ap_srb_to_setup_t *srbs; // reuse the same type as for setup; there is a
|
||||
// list for SRB modification
|
||||
|
||||
@@ -1666,6 +1666,15 @@ F1AP_F1AP_PDU_t *encode_ue_context_mod_req(const f1ap_ue_context_mod_req_t *req)
|
||||
OCTET_STRING_fromBuf(&ie81->value.choice.RRCContainer, (const char *)req->rrc_container->buf, req->rrc_container->len);
|
||||
}
|
||||
|
||||
/* optional: gNB-DU Configuration Query */
|
||||
if (req->gNB_DU_Configuration_Query && *req->gNB_DU_Configuration_Query) {
|
||||
asn1cSequenceAdd(out->protocolIEs.list, F1AP_UEContextModificationRequestIEs_t, ie82);
|
||||
ie82->id = F1AP_ProtocolIE_ID_id_GNB_DUConfigurationQuery;
|
||||
ie82->criticality = F1AP_Criticality_ignore;
|
||||
ie82->value.present = F1AP_UEContextModificationRequestIEs__value_PR_GNB_DUConfigurationQuery;
|
||||
ie82->value.choice.GNB_DUConfigurationQuery = F1AP_GNB_DUConfigurationQuery_true;
|
||||
}
|
||||
|
||||
/* optional: SRBs_ToBeSetupMod */
|
||||
if (req->srbs_len > 0) {
|
||||
DevAssert(req->srbs);
|
||||
@@ -1777,6 +1786,13 @@ bool decode_ue_context_mod_req(const F1AP_F1AP_PDU_t *pdu, f1ap_ue_context_mod_r
|
||||
*out->rrc_container = create_byte_array(os->size, os->buf);
|
||||
}
|
||||
break;
|
||||
case F1AP_ProtocolIE_ID_id_GNB_DUConfigurationQuery:
|
||||
_F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_GNB_DUConfigurationQuery);
|
||||
{
|
||||
bool v = ie->value.choice.GNB_DUConfigurationQuery == F1AP_GNB_DUConfigurationQuery_true;
|
||||
_F1_MALLOC(out->gNB_DU_Configuration_Query, v);
|
||||
}
|
||||
break;
|
||||
case F1AP_ProtocolIE_ID_id_SRBs_ToBeSetupMod_List:
|
||||
_F1_EQ_CHECK_INT(ie->value.present, F1AP_UEContextModificationRequestIEs__value_PR_SRBs_ToBeSetupMod_List);
|
||||
_F1_CHECK_EXP(decode_srbs_to_setupmod(&ie->value.choice.SRBs_ToBeSetupMod_List, &out->srbs_len, &out->srbs));
|
||||
@@ -1862,6 +1878,8 @@ f1ap_ue_context_mod_req_t cp_ue_context_mod_req(const f1ap_ue_context_mod_req_t
|
||||
}
|
||||
if (orig->status)
|
||||
_F1_MALLOC(cp.status, *orig->status);
|
||||
if (orig->gNB_DU_Configuration_Query)
|
||||
_F1_MALLOC(cp.gNB_DU_Configuration_Query, *orig->gNB_DU_Configuration_Query);
|
||||
return cp;
|
||||
}
|
||||
|
||||
@@ -1898,6 +1916,7 @@ bool eq_ue_context_mod_req(const f1ap_ue_context_mod_req_t *a, const f1ap_ue_con
|
||||
_F1_CHECK_EXP(eq_drb_to_release(&a->drbs_rel[i], &b->drbs_rel[i]));
|
||||
|
||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, status, _F1_EQ_CHECK_INT);
|
||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, gNB_DU_Configuration_Query, _F1_EQ_CHECK_INT);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1925,6 +1944,7 @@ void free_ue_context_mod_req(f1ap_ue_context_mod_req_t *req)
|
||||
free_drb_to_release(&req->drbs_rel[i]);
|
||||
free(req->drbs_rel);
|
||||
free(req->status);
|
||||
free(req->gNB_DU_Configuration_Query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1039,6 +1039,7 @@ static void test_f1ap_ue_context_modification_request()
|
||||
orig.drbs_rel[2].id = 19;
|
||||
|
||||
_F1_MALLOC(orig.status, LOWER_LAYERS_RESUME);
|
||||
_F1_MALLOC(orig.gNB_DU_Configuration_Query, true);
|
||||
|
||||
F1AP_F1AP_PDU_t *f1enc = encode_ue_context_mod_req(&orig);
|
||||
F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc);
|
||||
|
||||
@@ -607,6 +607,86 @@ static NR_UE_info_t *create_new_UE(gNB_MAC_INST *mac, uint32_t cu_id, const NR_C
|
||||
return UE;
|
||||
}
|
||||
|
||||
/** @brief Encode CellGroupConfig to byte array for transparent forwarding in F1AP messages.
|
||||
* The encoded bytes are intended for transparent forwarding to the UE without
|
||||
* decode/re-encode cycles per TS 38.473 transparency requirements.
|
||||
* @param cellGroup CellGroupConfig to encode
|
||||
* @return Encoded byte array */
|
||||
static byte_array_t encode_cellgroup_config(const NR_CellGroupConfig_t *cellGroup)
|
||||
{
|
||||
byte_array_t cgc = {0};
|
||||
ssize_t encoded = uper_encode_to_new_buffer(&asn_DEF_NR_CellGroupConfig, NULL, cellGroup, (void **)&cgc.buf);
|
||||
AssertFatal(encoded > 0, "Could not encode CellGroup\n");
|
||||
cgc.len = encoded;
|
||||
return cgc;
|
||||
}
|
||||
|
||||
/** @brief Handle RRC container from UE Context Setup/Modification request.
|
||||
* Per 3GPP TS 38.473, the RRCContainer IE contains an RRC message (e.g., DL-DCCH-Message
|
||||
* as defined in TS 38.331) that is encapsulated in a PDCP PDU. The DU forwards this
|
||||
* container transparently to the UE via SRB1 without decode/re-encode cycles.
|
||||
* @param rrc_container RRC container
|
||||
* @param rnti RNTI of the UE */
|
||||
static void handle_ue_context_rrc_container(const byte_array_t *rrc_container, const rnti_t rnti)
|
||||
{
|
||||
DevAssert(rrc_container);
|
||||
logical_chan_id_t id = 1;
|
||||
nr_rlc_srb_recv_sdu(rnti, id, rrc_container->buf, rrc_container->len);
|
||||
}
|
||||
|
||||
/** @brief Get and clone CellGroupConfig for UE Context Setup/Modification response.
|
||||
* The source depends on re-establishment state:
|
||||
* - CellGroup: Currently active/runtime CellGroupConfig used by MAC for scheduling.
|
||||
* During re-establishment, spCellConfig is removed from it (per TS 38.331 §5.3.7.2).
|
||||
* - reconfigCellGroup: Staging area that holds a complete CellGroupConfig saved before
|
||||
* modifications. During re-establishment, it still contains spCellConfig (saved before
|
||||
* removal). It becomes the new CellGroup when reconfiguration completes.
|
||||
* During re-establishment, we must clone from reconfigCellGroup to get a complete
|
||||
* CellGroupConfig with spCellConfig for transparent forwarding to the UE.
|
||||
* @param UE UE context
|
||||
* @return Cloned CellGroupConfig */
|
||||
static NR_CellGroupConfig_t *get_cellgroup_config(NR_UE_info_t *UE)
|
||||
{
|
||||
if (UE->reestablish_rlc && UE->reconfigCellGroup != NULL) {
|
||||
return clone_CellGroupConfig(UE->reconfigCellGroup);
|
||||
} else {
|
||||
return clone_CellGroupConfig(UE->CellGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Update CellGroupConfig for RRC re-establishment procedure.
|
||||
* This function prepares a complete CellGroupConfig for gNB-DU Configuration Query response
|
||||
* during RRC re-establishment.
|
||||
* The function sets reestablishRLC flags for all RLC bearers except SRB1.
|
||||
* SRB1 is removed from the bearer list since it is already re-established during
|
||||
* RRCReestablishment and should not be included. */
|
||||
static void update_cellgroup_for_reestablishment(NR_UE_info_t *UE, NR_CellGroupConfig_t *new_CellGroup)
|
||||
{
|
||||
DevAssert(new_CellGroup);
|
||||
DevAssert(new_CellGroup->spCellConfig);
|
||||
DevAssert(UE->reestablish_rlc);
|
||||
LOG_I(NR_MAC, "UE %04x: Re-establishment detected, setting reestablishRLC flags\n", UE->rnti);
|
||||
struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = new_CellGroup->rlc_BearerToAddModList;
|
||||
if (addmod && addmod->list.count > 0) {
|
||||
LOG_I(NR_MAC, "UE %04x: CellGroupConfig has %d bearers:\n", UE->rnti, addmod->list.count);
|
||||
for (int i = 0; i < addmod->list.count; ++i) {
|
||||
NR_RLC_BearerConfig_t *bearer = addmod->list.array[i];
|
||||
int lcid = bearer->logicalChannelIdentity;
|
||||
int rb_type = bearer->servedRadioBearer->present;
|
||||
int rb_id = (rb_type == NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity)
|
||||
? bearer->servedRadioBearer->choice.srb_Identity
|
||||
: bearer->servedRadioBearer->choice.drb_Identity;
|
||||
if (rb_type == NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity && rb_id == 1) {
|
||||
asn_sequence_del(&addmod->list, i, 1);
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
LOG_I(NR_MAC, "UE %04x: Re-establishing RLC for LCID %d\n", UE->rnti, lcid);
|
||||
asn1cCallocOne(addmod->list.array[i]->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
{
|
||||
const bool is_SA = IS_SA_MODE(get_softmodem_params());
|
||||
@@ -653,7 +733,8 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
AssertFatal(UE, "no UE found or could not be created, but UE Context Setup Failed not implemented\n");
|
||||
resp.gNB_DU_ue_id = UE->rnti;
|
||||
|
||||
NR_CellGroupConfig_t *new_CellGroup = clone_CellGroupConfig(UE->CellGroup);
|
||||
NR_CellGroupConfig_t *new_CellGroup = get_cellgroup_config(UE);
|
||||
|
||||
// Needed for DRB Setup (e.g., RLC might reduce SN size)
|
||||
UE->capability = ue_cap;
|
||||
|
||||
@@ -667,8 +748,7 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
}
|
||||
|
||||
if (req->rrc_container != NULL) {
|
||||
logical_chan_id_t id = 1;
|
||||
nr_rlc_srb_recv_sdu(UE->rnti, id, req->rrc_container->buf, req->rrc_container->len);
|
||||
handle_ue_context_rrc_container(req->rrc_container, UE->rnti);
|
||||
}
|
||||
|
||||
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
|
||||
@@ -678,6 +758,15 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
update_cellGroupConfig(new_CellGroup, UE->uid, UE->capability, &mac->radio_config, scc);
|
||||
}
|
||||
|
||||
/* During re-establishment, prepare CellGroupConfig for UE Context Setup response.
|
||||
* Per TS 38.401 §8.7: when a UE re-establishes on a different DU, the CU triggers
|
||||
* UE Context Setup on the new DU. The DU must respond with a CellGroupConfig that has
|
||||
* reestablishRLC flags set for all RLC bearers except SRB1. This prepares the CellGroupConfig
|
||||
* for transparent forwarding to the UE per TS 38.473 transparency requirements. */
|
||||
if (UE->reestablish_rlc) {
|
||||
update_cellgroup_for_reestablishment(UE, new_CellGroup);
|
||||
}
|
||||
|
||||
if (!ue_id_provided && cg_configinfo == NULL) {
|
||||
/* new UE: tell the UE to reestablish RLC */
|
||||
struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = new_CellGroup->rlc_BearerToAddModList;
|
||||
@@ -688,12 +777,7 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
}
|
||||
}
|
||||
|
||||
byte_array_t cgc = { .buf = calloc_or_fail(1,1024) };
|
||||
asn_enc_rval_t enc_rval =
|
||||
uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, new_CellGroup, cgc.buf, 1024);
|
||||
AssertFatal(enc_rval.encoded > 0, "Could not encode CellGroup, failed element %s\n", enc_rval.failed_type->name);
|
||||
cgc.len = (enc_rval.encoded + 7) >> 3;
|
||||
resp.du_to_cu_rrc_info.cell_group_config = cgc;
|
||||
resp.du_to_cu_rrc_info.cell_group_config = encode_cellgroup_config(new_CellGroup);
|
||||
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->reconfigCellGroup);
|
||||
UE->reconfigCellGroup = new_CellGroup;
|
||||
@@ -745,7 +829,7 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
return;
|
||||
}
|
||||
|
||||
NR_CellGroupConfig_t *new_CellGroup = clone_CellGroupConfig(UE->CellGroup);
|
||||
NR_CellGroupConfig_t *new_CellGroup = get_cellgroup_config(UE);
|
||||
|
||||
if (req->srbs_len > 0) {
|
||||
resp.srbs_len = handle_ue_context_srbs_setup(UE, req->srbs_len, req->srbs, &resp.srbs, new_CellGroup, &mac->rlc_config);
|
||||
@@ -760,8 +844,7 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
}
|
||||
|
||||
if (req->rrc_container != NULL) {
|
||||
logical_chan_id_t id = 1;
|
||||
nr_rlc_srb_recv_sdu(req->gNB_DU_ue_id, id, req->rrc_container->buf, req->rrc_container->len);
|
||||
handle_ue_context_rrc_container(req->rrc_container, req->gNB_DU_ue_id);
|
||||
}
|
||||
|
||||
NR_ServingCellConfigCommon_t *scc = mac->common_channels[0].ServingCellConfigCommon;
|
||||
@@ -771,6 +854,10 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
} else if (req->reconfig_compl) {
|
||||
LOG_I(NR_MAC, "DU received confirmation of successful RRC Reconfiguration\n");
|
||||
if (UE->reconfigCellGroup) {
|
||||
/** During handover, target DU never sends RRC Reconfiguration (source DU does),
|
||||
* so ack_reconfig is never called on target DU - this warning is expected.
|
||||
* If RRC Reconfiguration was sent via PDSCH, ack_reconfig should have been called when UE ACKed it.
|
||||
* If not, this warning would indicate a bug. */
|
||||
LOG_W(NR_MAC, "reconfigCellGroup still present, did we miss ACK for RRCReconfiguration?\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
|
||||
UE->CellGroup = UE->reconfigCellGroup;
|
||||
@@ -780,6 +867,7 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
for (int i = 1; i < seq_arr_size(&UE->UE_sched_ctrl.lc_config); ++i) {
|
||||
nr_lc_config_t *c = seq_arr_at(&UE->UE_sched_ctrl.lc_config, i);
|
||||
c->suspended = false;
|
||||
LOG_I(NR_MAC, "UE %04x: Re-establishing RLC for LCID %d\n", UE->rnti, c->lcid);
|
||||
nr_rlc_reestablish_entity(req->gNB_DU_ue_id, c->lcid);
|
||||
}
|
||||
UE->reestablish_rlc = false;
|
||||
@@ -797,18 +885,21 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
update_cellGroupConfig(new_CellGroup, UE->uid, UE->capability, &mac->radio_config, scc);
|
||||
}
|
||||
|
||||
if (req->srbs_len > 0 || req->drbs_len > 0 || req->drbs_rel_len > 0 || ue_cap != NULL) {
|
||||
resp.du_to_cu_rrc_info = calloc_or_fail(1, sizeof(du_to_cu_rrc_information_t));
|
||||
byte_array_t cgc = { .buf = calloc_or_fail(1, 1024) };
|
||||
asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig,
|
||||
NULL,
|
||||
new_CellGroup,
|
||||
cgc.buf,
|
||||
1024);
|
||||
AssertFatal(enc_rval.encoded > 0, "Could not encode CellGroup, failed element %s\n", enc_rval.failed_type->name);
|
||||
cgc.len = (enc_rval.encoded + 7) >> 3;
|
||||
resp.du_to_cu_rrc_info->cell_group_config = cgc;
|
||||
/* 3GPP TS 38.473 Clause 8.3.4: If gNB-DU Configuration Query is present, include CellGroupConfig
|
||||
* (CU requested CellGroupConfig for transparent forwarding) */
|
||||
if (req->gNB_DU_Configuration_Query != NULL && *req->gNB_DU_Configuration_Query) {
|
||||
LOG_I(NR_MAC, "UE %04x: gNB-DU Configuration Query received, will include CellGroupConfig in response\n", UE->rnti);
|
||||
|
||||
if (UE->reestablish_rlc) {
|
||||
update_cellgroup_for_reestablishment(UE, new_CellGroup);
|
||||
}
|
||||
|
||||
/* Encode CellGroupConfig for transparent forwarding in the DU to CU trasnfer IE
|
||||
* CU will forward these encoded bytes directly to UE without decode/re-encode cycles */
|
||||
resp.du_to_cu_rrc_info = calloc_or_fail(1, sizeof(du_to_cu_rrc_information_t));
|
||||
resp.du_to_cu_rrc_info->cell_group_config = encode_cellgroup_config(new_CellGroup);
|
||||
|
||||
// Replace reconfigCellGroup with new_CellGroup (now contains complete config with spCellConfig restored)
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->reconfigCellGroup);
|
||||
UE->reconfigCellGroup = new_CellGroup;
|
||||
configure_UE_BWP(mac, scc, UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1);
|
||||
@@ -915,6 +1006,11 @@ void ue_context_release_command(const f1ap_ue_context_rel_cmd_t *cmd)
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
}
|
||||
|
||||
/** @brief Process a DL RRC MESSAGE TRANSFER. Handles delivery of an RRC message to a UE
|
||||
* via the F1AP DL RRC MESSAGE TRANSFER procedure, as specified in TS 38.473. This procedure
|
||||
* is also responsible for re-establishing UE context when required (e.g., during RRC connection
|
||||
* reestablishment).
|
||||
* @param dl_rrc Pointer to the DL RRC MESSAGE TRANSFER data structure. */
|
||||
void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc)
|
||||
{
|
||||
LOG_D(NR_MAC,
|
||||
@@ -942,50 +1038,71 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc)
|
||||
DevAssert(success);
|
||||
}
|
||||
|
||||
|
||||
/* if we get the old-gNB-DU-UE-ID, this means there is a reestablishment
|
||||
* ongoing. */
|
||||
/* Per TS 38.473: "The DL RRC MESSAGE TRANSFER message shall include, if available,
|
||||
* the old gNB-DU UE F1AP ID IE so that the gNB-DU can retrieve the existing UE context
|
||||
* in RRC connection reestablishment procedure, as defined in TS 38.401"
|
||||
*
|
||||
* "If the gNB-DU identifies the UE-associated logical F1-connection by the gNB-DU UE F1AP ID
|
||||
* IE in the DL RRC MESSAGE TRANSFER message and the old gNB-DU UE F1AP ID IE is included,
|
||||
* it shall release the old gNB-DU UE F1AP ID and the related configurations associated
|
||||
* with the old gNB-DU UE F1AP ID." */
|
||||
if (dl_rrc->old_gNB_DU_ue_id != NULL) {
|
||||
AssertFatal(*dl_rrc->old_gNB_DU_ue_id != dl_rrc->gNB_DU_ue_id,
|
||||
"logic bug: current and old gNB DU UE ID cannot be the same\n");
|
||||
/* 38.401 says: "Find UE context based on old gNB-DU UE F1AP ID, replace
|
||||
* old C-RNTI/PCI with new C-RNTI/PCI". Below, we do the inverse: we keep
|
||||
* the new UE context (with new C-RNTI), but set up everything to reuse the
|
||||
* old config. */
|
||||
NR_UE_info_t *oldUE = find_nr_UE(&mac->UE_info, *dl_rrc->old_gNB_DU_ue_id);
|
||||
AssertFatal(oldUE, "CU claims we should know UE %04x, but we don't\n", *dl_rrc->old_gNB_DU_ue_id);
|
||||
pthread_mutex_lock(&mac->sched_lock);
|
||||
uid_t temp_uid = UE->uid;
|
||||
UE->uid = oldUE->uid;
|
||||
oldUE->uid = temp_uid;
|
||||
for (int i = 1; i < seq_arr_size(&oldUE->UE_sched_ctrl.lc_config); ++i) {
|
||||
const nr_lc_config_t *c = seq_arr_at(&oldUE->UE_sched_ctrl.lc_config, i);
|
||||
nr_lc_config_t new = *c;
|
||||
new.suspended = true;
|
||||
nr_mac_add_lcid(&UE->UE_sched_ctrl, &new);
|
||||
if (oldUE == NULL) {
|
||||
/* No matching UE-associated logical F1-connection for the old gNB-DU UE F1AP ID.
|
||||
* Per TS 38.473, if there's no matching connection, there's nothing to release. */
|
||||
LOG_I(NR_MAC,
|
||||
"DL RRC Message Transfer: old gNB-DU UE F1AP ID %04x has no matching UE-associated logical F1-connection, nothing to "
|
||||
"release\n",
|
||||
*dl_rrc->old_gNB_DU_ue_id);
|
||||
/* Clean up any F1 UE data associated with the old gNB-DU UE F1AP ID */
|
||||
if (du_exists_f1_ue_data(*dl_rrc->old_gNB_DU_ue_id)) {
|
||||
du_remove_f1_ue_data(*dl_rrc->old_gNB_DU_ue_id);
|
||||
}
|
||||
} else {
|
||||
/* Per TS 38.401: "Find UE context based on old gNB-DU UE F1AP ID, replace
|
||||
* old C-RNTI/PCI with new C-RNTI/PCI". Below, we do the inverse: we keep
|
||||
* the new UE context (with new C-RNTI), but set up everything to reuse the
|
||||
* old config. */
|
||||
pthread_mutex_lock(&mac->sched_lock);
|
||||
uid_t temp_uid = UE->uid;
|
||||
UE->uid = oldUE->uid;
|
||||
oldUE->uid = temp_uid;
|
||||
for (int i = 1; i < seq_arr_size(&oldUE->UE_sched_ctrl.lc_config); ++i) {
|
||||
const nr_lc_config_t *c = seq_arr_at(&oldUE->UE_sched_ctrl.lc_config, i);
|
||||
nr_lc_config_t new = *c;
|
||||
new.suspended = true;
|
||||
nr_mac_add_lcid(&UE->UE_sched_ctrl, &new);
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
|
||||
UE->CellGroup = oldUE->CellGroup;
|
||||
oldUE->CellGroup = NULL;
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->capability);
|
||||
UE->capability = oldUE->capability;
|
||||
oldUE->capability = NULL;
|
||||
UE->mac_stats = oldUE->mac_stats;
|
||||
UE->measgap_config = oldUE->measgap_config;
|
||||
UE->local_bwp_id = oldUE->local_bwp_id;
|
||||
mac_remove_nr_ue(mac, *dl_rrc->old_gNB_DU_ue_id);
|
||||
pthread_mutex_unlock(&mac->sched_lock);
|
||||
nr_rlc_remove_ue(dl_rrc->gNB_DU_ue_id);
|
||||
nr_rlc_update_id(*dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id);
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
if (f1inst >= 0) // we actually use F1-U
|
||||
gtpv1u_update_ue_id(f1inst, *dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id);
|
||||
}
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->CellGroup);
|
||||
UE->CellGroup = oldUE->CellGroup;
|
||||
oldUE->CellGroup = NULL;
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->capability);
|
||||
UE->capability = oldUE->capability;
|
||||
oldUE->capability = NULL;
|
||||
UE->mac_stats = oldUE->mac_stats;
|
||||
UE->measgap_config = oldUE->measgap_config;
|
||||
UE->local_bwp_id = oldUE->local_bwp_id;
|
||||
/* 38.331 5.3.7.2 says that the UE releases the spCellConfig, so we drop it
|
||||
/* Per TS 38.331 5.3.7.2: the UE releases the spCellConfig, so we drop it
|
||||
* from the current configuration. It will be reapplied when the
|
||||
* reconfiguration has succeeded (indicated by the CU) */
|
||||
asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&UE->reconfigCellGroup, UE->CellGroup);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_SpCellConfig, UE->CellGroup->spCellConfig);
|
||||
UE->CellGroup->spCellConfig = NULL;
|
||||
UE->reestablish_rlc = true;
|
||||
mac_remove_nr_ue(mac, *dl_rrc->old_gNB_DU_ue_id);
|
||||
pthread_mutex_unlock(&mac->sched_lock);
|
||||
nr_rlc_remove_ue(dl_rrc->gNB_DU_ue_id);
|
||||
nr_rlc_update_id(*dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id);
|
||||
/* 38.331 clause 5.3.7.4: apply gNB RLC configuration for SRB1 to match the UE RLC configuration defined in 9.2.1 */
|
||||
nr_rlc_configuration_t rlc_configuration = mac->rlc_config; // use configuration file values for timers t_poll_retransmit, t_reassembly and t_status_prohibit
|
||||
/* Per TS 38.331 clause 5.3.7.4: apply gNB RLC configuration for SRB1 to match the UE RLC configuration defined in 9.2.1.
|
||||
* Use configuration file values for timers t_poll_retransmit, t_reassembly and t_status_prohibit */
|
||||
nr_rlc_configuration_t rlc_configuration = mac->rlc_config;
|
||||
rlc_configuration.srb.poll_pdu = -1;
|
||||
rlc_configuration.srb.poll_byte = -1;
|
||||
rlc_configuration.srb.max_retx_threshold = 8;
|
||||
@@ -993,9 +1110,6 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc)
|
||||
NR_RLC_Config_t *rlc_Config = nr_srb_config(&rlc_configuration);
|
||||
nr_rlc_reconfigure_entity(dl_rrc->gNB_DU_ue_id, 1, rlc_Config);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_RLC_Config, rlc_Config);
|
||||
instance_t f1inst = get_f1_gtp_instance();
|
||||
if (f1inst >= 0) // we actually use F1-U
|
||||
gtpv1u_update_ue_id(f1inst, *dl_rrc->old_gNB_DU_ue_id, dl_rrc->gNB_DU_ue_id);
|
||||
}
|
||||
|
||||
/* the DU ue id is the RNTI */
|
||||
|
||||
@@ -506,6 +506,7 @@ srb_found:
|
||||
* RRC, the RLC-PDCP interface does not transport this information */
|
||||
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->ue_id);
|
||||
ul_rrc->gNB_DU_ue_id = ue_data.secondary_ue;
|
||||
message_p->ittiMsgHeader.originInstance = ue_data.du_assoc_id;
|
||||
ul_rrc->srb_id = srb_id;
|
||||
ul_rrc->rrc_container = malloc(size);
|
||||
AssertFatal(ul_rrc->rrc_container != NULL, "OUT OF MEMORY\n");
|
||||
|
||||
@@ -532,7 +532,7 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re
|
||||
ie->measConfig = params->meas_config;
|
||||
|
||||
/* nonCriticalExtension, RRCReconfiguration-v1530-IEs */
|
||||
if (params->cell_group_config || params->num_nas_msg) {
|
||||
if (params->cgc || params->num_nas_msg) {
|
||||
// Allocate memory for extension IE
|
||||
ie->nonCriticalExtension = calloc_or_fail(1, sizeof(*ie->nonCriticalExtension));
|
||||
}
|
||||
@@ -549,37 +549,13 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re
|
||||
}
|
||||
}
|
||||
|
||||
/* masterCellGroup */
|
||||
if (params->cell_group_config) {
|
||||
uint8_t temp[4096];
|
||||
asn_enc_rval_t enc = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, params->cell_group_config, temp, sizeof(temp));
|
||||
|
||||
if (enc.encoded <= 0) {
|
||||
LOG_E(NR_RRC, "ASN.1 encoding failed for NR_CellGroupConfig (encoded=%ld)\n", enc.encoded);
|
||||
if (enc.failed_type) {
|
||||
LOG_E(NR_RRC, "Failed at ASN.1 type: %s\n", enc.failed_type->name);
|
||||
}
|
||||
if (enc.structure_ptr) {
|
||||
LOG_E(NR_RRC, "Failed at structure element: %p\n", enc.structure_ptr);
|
||||
}
|
||||
// Print diagnostic information to help debug the issue
|
||||
LOG_E(NR_RRC, "CellGroupConfig structure that failed to encode:\n");
|
||||
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *)params->cell_group_config);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration_IEs, ie);
|
||||
return NULL;
|
||||
}
|
||||
DevAssert(enc.encoded <= sizeof(temp) * 8); // Encoded data must fit in temp buffer
|
||||
// Allocate buffer for the encoded data and copy it
|
||||
// enc.encoded is in bits, convert to bytes
|
||||
size_t encoded_bytes = (enc.encoded + 7) / 8;
|
||||
uint8_t *buf = calloc_or_fail(1, encoded_bytes);
|
||||
memcpy(buf, temp, encoded_bytes);
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *)params->cell_group_config);
|
||||
}
|
||||
/* masterCellGroup - Transparent forwarding per TS 38.473 */
|
||||
if (params->cgc) {
|
||||
// Copy pre-encoded CellGroupConfig bytes directly without decode/re-encode (TS 38.473 transparency)
|
||||
// CU receives encoded bytes from DU and forwards to UE without modification
|
||||
LOG_D(NR_RRC, "Transparent forwarding CellGroupConfig (len=%ld bytes)\n", params->cgc->len);
|
||||
ie->nonCriticalExtension->masterCellGroup = calloc_or_fail(1, sizeof(*ie->nonCriticalExtension->masterCellGroup));
|
||||
*ie->nonCriticalExtension->masterCellGroup = (OCTET_STRING_t){.buf = buf, .size = encoded_bytes};
|
||||
OCTET_STRING_fromBuf(ie->nonCriticalExtension->masterCellGroup, (const char *)params->cgc->buf, params->cgc->len);
|
||||
}
|
||||
|
||||
/* masterKeyUpdate */
|
||||
@@ -630,6 +606,49 @@ static byte_array_t do_HO_RRCReconfiguration(nr_rrc_reconfig_param_t *params)
|
||||
return msg;
|
||||
}
|
||||
|
||||
void dump_cgc(const uint8_t *buf, size_t len)
|
||||
{
|
||||
// Decode the encoded CellGroupConfig for debugging
|
||||
NR_CellGroupConfig_t *temp_cellGroupConfig = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&temp_cellGroupConfig, buf, len);
|
||||
if (dec_rval.code == RC_OK && dec_rval.consumed > 0) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, temp_cellGroupConfig);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, temp_cellGroupConfig);
|
||||
} else {
|
||||
LOG_W(NR_RRC, "Failed to decode CellGroupConfig (code=%d consumed=%zu)\n", dec_rval.code, dec_rval.consumed);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_mcg(const NR_DL_DCCH_Message_t *dl_dcch_msg)
|
||||
{
|
||||
if (!dl_dcch_msg) {
|
||||
LOG_W(NR_RRC, "DL_DCCH_Message is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const NR_RRCReconfiguration_IEs_t *reconf_ies = NULL;
|
||||
const struct NR_DL_DCCH_MessageType__c1 *c1 = NULL;
|
||||
|
||||
if (dl_dcch_msg->message.present == NR_DL_DCCH_MessageType_PR_c1
|
||||
&& (c1 = dl_dcch_msg->message.choice.c1)
|
||||
&& c1->present == NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration
|
||||
&& c1->choice.rrcReconfiguration
|
||||
&& c1->choice.rrcReconfiguration->criticalExtensions.present
|
||||
== NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration
|
||||
&& c1->choice.rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration) {
|
||||
reconf_ies = c1->choice.rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration;
|
||||
}
|
||||
|
||||
if (reconf_ies && reconf_ies->nonCriticalExtension && reconf_ies->nonCriticalExtension->masterCellGroup) {
|
||||
const OCTET_STRING_t *mcg = reconf_ies->nonCriticalExtension->masterCellGroup;
|
||||
|
||||
/* decode and XER print CellGroupConfig */
|
||||
dump_cgc(mcg->buf, mcg->size);
|
||||
} else {
|
||||
LOG_W(NR_RRC, "No masterCellGroup found in RRCReconfiguration nonCriticalExtension\n");
|
||||
}
|
||||
}
|
||||
|
||||
byte_array_t do_RRCReconfiguration(const nr_rrc_reconfig_param_t *params)
|
||||
{
|
||||
byte_array_t msg = {.buf = NULL, .len = 0};
|
||||
@@ -651,6 +670,7 @@ byte_array_t do_RRCReconfiguration(const nr_rrc_reconfig_param_t *params)
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
|
||||
xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg);
|
||||
dump_mcg(&dl_dcch_msg);
|
||||
}
|
||||
|
||||
int val = uper_encode_to_new_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, &dl_dcch_msg, (void **)&msg.buf);
|
||||
|
||||
@@ -70,7 +70,7 @@ typedef struct {
|
||||
NR_MeasConfig_t *meas_config;
|
||||
byte_array_t dedicated_NAS_msg_list[MAX_DRBS_PER_UE];
|
||||
int num_nas_msg;
|
||||
NR_CellGroupConfig_t *cell_group_config;
|
||||
byte_array_t *cgc;
|
||||
bool masterKeyUpdate;
|
||||
int nextHopChainingCount;
|
||||
byte_array_t ue_cap;
|
||||
@@ -179,5 +179,6 @@ byte_array_t doRRCReconfiguration_from_HandoverCommand(byte_array_t handoverComm
|
||||
|
||||
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);
|
||||
void dump_cgc(const uint8_t *buf, size_t len);
|
||||
|
||||
#endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */
|
||||
|
||||
@@ -186,7 +186,8 @@ typedef struct gNB_RRC_UE_s {
|
||||
NR_UE_MRDC_Capability_t* UE_Capability_MRDC;
|
||||
int UE_MRDC_Capability_size;
|
||||
|
||||
NR_CellGroupConfig_t *masterCellGroup;
|
||||
// Transparent forwarding of CellGroupConfig
|
||||
byte_array_t mcg;
|
||||
NR_RadioBearerConfig_t *rb_config;
|
||||
|
||||
/* KgNB as derived from KASME received from EPC */
|
||||
|
||||
@@ -100,9 +100,9 @@ int rrc_gNB_generate_pcch_msg(sctp_assoc_t assoc_id, const NR_SIB1_t *sib, uint3
|
||||
|
||||
/* UE Management Procedures */
|
||||
|
||||
void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc,
|
||||
rrc_gNB_ue_context_t *const ue_context_pP,
|
||||
const e1ap_bearer_setup_resp_t *resp);
|
||||
void rrc_f1_ue_context_setup_from_e1_response(const gNB_RRC_INST *rrc,
|
||||
rrc_gNB_ue_context_t *const ue_context_pP,
|
||||
const e1ap_bearer_setup_resp_t *resp);
|
||||
|
||||
void rrc_gNB_generate_UeContextModificationRequest(const gNB_RRC_INST *rrc,
|
||||
rrc_gNB_ue_context_t *const ue_context_pP,
|
||||
@@ -124,4 +124,14 @@ void activate_srb(gNB_RRC_UE_t *UE, int srb_id);
|
||||
void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_status_t *drb_status);
|
||||
void init_delayed_action(delayed_action_state_t *delayed_action);
|
||||
|
||||
/** @brief Prepare and send F1AP UE Context Setup Request for a target DU
|
||||
* @param rrc RRC instance
|
||||
* @param ue UE context
|
||||
* @param du Target DU container containing cell and setup information
|
||||
* @param ho_prep_info Optional handover preparation information (NULL if not provided) */
|
||||
void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *ue,
|
||||
const nr_rrc_du_container_t *du,
|
||||
const byte_array_t *ho_prep_info);
|
||||
|
||||
#endif
|
||||
|
||||
+521
-255
File diff suppressed because it is too large
Load Diff
@@ -91,52 +91,6 @@ void nr_rrc_apply_target_context(gNB_RRC_UE_t *UE)
|
||||
UE->nr_cellid = target_ctx->du->setup_req->cell[0].info.nr_cellid;
|
||||
}
|
||||
|
||||
/** @brief Fill DRB to Be Setup List in F1 UE Context Setup Request (optional list)
|
||||
* @return 0 if list is empty, list size otherwise */
|
||||
static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_drb_to_setup_t drbs[MAX_DRBS_PER_UE])
|
||||
{
|
||||
int nb_drb = 0;
|
||||
|
||||
FOR_EACH_SEQ_ARR(drb_t *, rrc_drb, &ue->drbs) {
|
||||
DevAssert(nb_drb < MAX_DRBS_PER_UE);
|
||||
f1ap_drb_to_setup_t *drb = &drbs[nb_drb];
|
||||
nr_pdcp_configuration_t *pdcp = &rrc_drb->pdcp_config;
|
||||
nb_drb++;
|
||||
/* fetch an existing PDU session for this DRB */
|
||||
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue, rrc_drb->drb_id);
|
||||
AssertFatal(pdu != NULL, "no PDU session for DRB ID %d\n", rrc_drb->drb_id);
|
||||
|
||||
drb->id = rrc_drb->drb_id;
|
||||
|
||||
drb->qos_choice = F1AP_QOS_CHOICE_NR;
|
||||
drb->nr.nssai = pdu->param.nssai;
|
||||
drb->nr.flows_len = 1;
|
||||
drb->nr.flows = calloc_or_fail(1, sizeof(*drb->nr.flows));
|
||||
|
||||
// Find the QoS flow associated with this DRB
|
||||
// Since we don't have QFI mapping in the new structure, we'll use the first QoS flow
|
||||
AssertFatal(seq_arr_size(&pdu->param.qos) == 1, "only 1 Qos flow supported\n");
|
||||
nr_rrc_qos_t *qos_param = (nr_rrc_qos_t *)seq_arr_at(&pdu->param.qos, 0);
|
||||
DevAssert(qos_param->qos.qfi > 0);
|
||||
drb->nr.flows[0].qfi = qos_param->qos.qfi;
|
||||
drb->nr.flows[0].param = get_qos_char_from_qos_flow_param(&qos_param->qos);
|
||||
/* the DRB QoS parameters: we just reuse the ones from the first flow */
|
||||
drb->nr.drb_qos = drb->nr.flows[0].param;
|
||||
|
||||
memcpy(&drb->up_ul_tnl[0].tl_address, &rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(uint8_t) * 4);
|
||||
drb->up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.teid;
|
||||
drb->up_ul_tnl_len = 1;
|
||||
|
||||
drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM;
|
||||
DevAssert(pdcp->drb.sn_size == 18 || pdcp->drb.sn_size == 12);
|
||||
drb->dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb->dl_pdcp_sn_len));
|
||||
*drb->dl_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
|
||||
drb->ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb->ul_pdcp_sn_len));
|
||||
*drb->ul_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
|
||||
}
|
||||
return nb_drb;
|
||||
}
|
||||
|
||||
/* \brief Initiate a handover of UE to a specific target cell handled by this
|
||||
* CU.
|
||||
* \param ue a UE context for which the handover should be triggered. The UE
|
||||
@@ -194,8 +148,8 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
ho_ctx->source->old_du_tunnel_config = drb->du_tunnel_config;
|
||||
}
|
||||
|
||||
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");
|
||||
// Store the old CellGroupConfig for reestablishment
|
||||
ho_ctx->source->old_cgc = copy_byte_array(ue->mcg);
|
||||
}
|
||||
|
||||
LOG_A(NR_RRC,
|
||||
@@ -206,48 +160,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
target_du->assoc_id,
|
||||
target_du->setup_req->cell[0].info.nr_pci);
|
||||
|
||||
f1ap_drb_to_setup_t *drbs = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(*drbs));
|
||||
int nb_drb = fill_drb_to_be_setup(rrc, ue, drbs);
|
||||
|
||||
int nb_srb = 2;
|
||||
f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs));
|
||||
srbs[0].id = 1;
|
||||
srbs[1].id = 2;
|
||||
|
||||
free_MeasConfig(ue->measConfig);
|
||||
ue->measConfig = nr_rrc_get_measconfig(rrc, target_du->setup_req->cell[0].info.nr_cellid);
|
||||
byte_array_t *meas_config = calloc_or_fail(1, sizeof(*meas_config));
|
||||
meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE);
|
||||
|
||||
byte_array_t *meas_timing_config = get_meas_timing_config(target_du->mtc, ue->measConfig);
|
||||
|
||||
byte_array_t *hpi = malloc_or_fail(sizeof(*hpi));
|
||||
*hpi = copy_byte_array(*ho_prep_info);
|
||||
|
||||
uint64_t *ue_agg_mbr = malloc_or_fail(sizeof(*ue_agg_mbr));
|
||||
*ue_agg_mbr = 1000000000 /*bps*/;
|
||||
|
||||
f1ap_served_cell_info_t *cell_info = &target_du->setup_req->cell[0].info;
|
||||
RETURN_IF_INVALID_ASSOC_ID(target_du->assoc_id);
|
||||
f1ap_ue_context_setup_req_t ue_context_setup_req = {
|
||||
.gNB_CU_ue_id = ue->rrc_ue_id,
|
||||
.plmn.mcc = cell_info->plmn.mcc,
|
||||
.plmn.mnc = cell_info->plmn.mnc,
|
||||
.plmn.mnc_digit_length = cell_info->plmn.mnc_digit_length,
|
||||
.nr_cellid = cell_info->nr_cellid,
|
||||
.servCellIndex = 0, // TODO: correct value?
|
||||
.srbs_len = nb_srb,
|
||||
.srbs = srbs,
|
||||
.drbs_len = nb_drb,
|
||||
.drbs = drbs,
|
||||
.cu_to_du_rrc_info.ho_prep_info = hpi,
|
||||
.cu_to_du_rrc_info.meas_config = meas_config,
|
||||
.cu_to_du_rrc_info.meas_timing_config = meas_timing_config,
|
||||
.gnb_du_ue_agg_mbr_ul = ue_agg_mbr,
|
||||
};
|
||||
rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req);
|
||||
free_ue_context_setup_req(&ue_context_setup_req);
|
||||
rrc_f1_ue_context_setup_for_target_du(rrc, ue, target_du, ho_prep_info);
|
||||
}
|
||||
|
||||
typedef struct deliver_ue_ctxt_modification_data_t {
|
||||
@@ -392,7 +305,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
|
||||
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue)
|
||||
{
|
||||
if (ue->ho_context->source)
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, ue->ho_context->source->old_cellGroupConfig);
|
||||
free_byte_array(ue->ho_context->source->old_cgc);
|
||||
free_ho_ctx(ue->ho_context);
|
||||
ue->ho_context = NULL;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ typedef struct nr_ho_source_cu {
|
||||
rnti_t old_rnti;
|
||||
/// old (source) CellGroupConfig (to send the cellGroupConfig in case of
|
||||
/// reestablishment)
|
||||
NR_CellGroupConfig_t *old_cellGroupConfig;
|
||||
byte_array_t old_cgc;
|
||||
/// function pointer to announce the handover cancellation, e.g.,
|
||||
/// reestablishment
|
||||
ho_cancel_t ho_cancel;
|
||||
|
||||
@@ -386,18 +386,15 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc, x2ap_ENDC_sgnb_addition_req_t *m, sctp_
|
||||
free_ue_context_setup_req(&req);
|
||||
}
|
||||
|
||||
static NR_RRCReconfiguration_IEs_t *get_default_reconfig(const NR_CellGroupConfig_t *secondaryCellGroup)
|
||||
static NR_RRCReconfiguration_IEs_t *get_default_reconfig(const byte_array_t cgc)
|
||||
{
|
||||
NR_RRCReconfiguration_IEs_t *reconfig = calloc(1, sizeof(NR_RRCReconfiguration_IEs_t));
|
||||
AssertFatal(reconfig != NULL, "out of memory\n");
|
||||
AssertFatal(secondaryCellGroup != NULL, "secondaryCellGroup is null\n");
|
||||
AssertFatal(cgc.buf != NULL, "cgc is null\n");
|
||||
reconfig->radioBearerConfig = NULL;
|
||||
|
||||
char scg_buffer[1024];
|
||||
asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, (void *)secondaryCellGroup, scg_buffer, 1024);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
reconfig->secondaryCellGroup = calloc(1, sizeof(*reconfig->secondaryCellGroup));
|
||||
OCTET_STRING_fromBuf(reconfig->secondaryCellGroup, (const char *)scg_buffer, (enc_rval.encoded + 7) >> 3);
|
||||
// Copy the stored CellGroupConfig to the RRCReconfiguration message
|
||||
reconfig->secondaryCellGroup = calloc_or_fail(1, sizeof(*reconfig->secondaryCellGroup));
|
||||
OCTET_STRING_fromBuf(reconfig->secondaryCellGroup, (const char *)cgc.buf, cgc.len);
|
||||
reconfig->measConfig = NULL;
|
||||
reconfig->lateNonCriticalExtension = NULL;
|
||||
reconfig->nonCriticalExtension = NULL;
|
||||
@@ -469,7 +466,7 @@ void rrc_add_nsa_user_resp(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const f1ap_ue_co
|
||||
NR_RRCReconfiguration_t *reconfig = calloc(1, sizeof(NR_RRCReconfiguration_t));
|
||||
reconfig->rrc_TransactionIdentifier = 0;
|
||||
reconfig->criticalExtensions.present = NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration;
|
||||
reconfig->criticalExtensions.choice.rrcReconfiguration = get_default_reconfig(UE->masterCellGroup);
|
||||
reconfig->criticalExtensions.choice.rrcReconfiguration = get_default_reconfig(UE->mcg);
|
||||
|
||||
NR_CG_Config_t *CG_Config = generate_CG_Config(reconfig, UE->rb_config);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration, reconfig);
|
||||
|
||||
@@ -2617,7 +2617,7 @@ static int nr_rrc_ue_decode_dcch(NR_UE_RRC_INST_t *rrc,
|
||||
set_DRB_status(rrc, i, RB_ESTABLISHED);
|
||||
nr_mac_rrc_message_t rrc_msg = {0};
|
||||
rrc_msg.payload_type = NR_MAC_RRC_RESUME_RB;
|
||||
rrc_msg.payload.resume_rb.is_srb = true;
|
||||
rrc_msg.payload.resume_rb.is_srb = false;
|
||||
rrc_msg.payload.resume_rb.rb_id = i;
|
||||
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user