GTPU: add an API to better deal with extension headers

And adapt existing code to use it.

Only for transmission for the moment.
This commit is contained in:
Cedric Roux
2025-08-21 09:30:41 +02:00
committed by Robert Schmidt
parent 33cbbafddb
commit c35f07b0b9
4 changed files with 276 additions and 70 deletions
+2 -1
View File
@@ -1395,9 +1395,10 @@ include_directories("${OPENAIR1_DIR}/SCHED_NR_UE")
add_library (GTPV1U
${RRC_DIR}/rrc_eNB_GTPV1U.c
${OPENAIR3_DIR}/ocp-gtpu/gtp_itf.cpp
${OPENAIR3_DIR}/ocp-gtpu/gtpu_extensions.c
)
target_link_libraries(GTPV1U PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs)
target_link_libraries(GTPV1U PRIVATE SIMU)
target_link_libraries(GTPV1U PRIVATE SIMU ds)
include_directories(${OPENAIR3_DIR}/ocp-gtp)
set (MME_APP_SRC
+62 -69
View File
@@ -21,6 +21,7 @@ extern "C" {
#include <openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h>
#include <openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h>
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
#include "openair3/ocp-gtpu/gtpu_extensions.h"
#include "sim.h"
#pragma pack(1)
@@ -230,11 +231,9 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer,
int msgLen,
bool seqNumFlag,
bool npduNumFlag,
int extHdrType,
uint8_t *extensionHeader_buffer,
uint8_t extensionHeader_length)
gtpu_extension_header_t *extensions,
int extensions_count)
{
DevAssert(msgLen + HDR_MAX < 65536); // maximum size of UDP packet
uint8_t buffer[msgLen + HDR_MAX];
uint8_t *curPtr = buffer;
@@ -242,7 +241,7 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer,
// N should be 0 for us (it was used only in 2G and 3G)
msgHdr->PN = npduNumFlag;
msgHdr->S = seqNumFlag;
msgHdr->E = extHdrType != NO_MORE_EXT_HDRS;
msgHdr->E = extensions_count != 0;
msgHdr->spare = 0;
// PT=0 is for GTP' TS 32.295 (charging)
msgHdr->PT = 1;
@@ -252,32 +251,32 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer,
curPtr += sizeof(Gtpv1uMsgHeaderT);
if (seqNumFlag || (extHdrType != NO_MORE_EXT_HDRS) || npduNumFlag) {
if (msgHdr->PN || msgHdr->S || msgHdr->E) {
*(uint16_t *)curPtr = seqNumFlag ? bearer->seqNum : 0x0000;
curPtr += sizeof(uint16_t);
*(uint8_t *)curPtr = npduNumFlag ? bearer->npduNum : 0x00;
curPtr++;
*(uint8_t *)curPtr = extHdrType;
*curPtr = extensions_count ? serialize_gtpu_extension_type(extensions[0].type) : 0;
curPtr++;
}
// Bug: if there is more than one extension, infinite loop on extensionHeader_buffer
while (extHdrType != NO_MORE_EXT_HDRS) {
if (extensionHeader_length > 0) {
memcpy(curPtr, extensionHeader_buffer, extensionHeader_length);
curPtr += extensionHeader_length;
LOG_D(GTPU,
"Extension Header for DDD added. The length is: %d, extension header type is: %x \n",
extensionHeader_length,
*((uint8_t *)(buffer + 11)));
extHdrType = extensionHeader_buffer[extensionHeader_length - 1];
LOG_D(GTPU, "Next extension header type is: %x \n", *((uint8_t *)(buffer + 11)));
} else {
LOG_W(GTPU, "Extension header type not supported, returning... \n");
for (int i = 0; i < extensions_count; i++) {
int available_size = sizeof(buffer) - (curPtr - buffer);
gtpu_extension_header_type_t next = i == extensions_count - 1 ? GTPU_EXT_NONE : extensions[i + 1].type;
int len = serialize_extension(&extensions[i], next, curPtr, available_size);
if (len == -1) {
LOG_E(GTPU, "GTP extension serialization: buffer too small\n");
return GTPNOK;
}
curPtr += len;
}
if (Msg != NULL) {
int available_size = sizeof(buffer) - (curPtr - buffer);
if (msgLen > available_size) {
LOG_E(GTPU, "GTP message creation: buffer too small\n");
return GTPNOK;
}
memcpy(curPtr, Msg, msgLen);
curPtr += msgLen;
}
@@ -347,18 +346,22 @@ void gtpv1uSendDirect(instance_t instance,
gtpv1u_bearer_t bearer = ptr2->second;
pthread_mutex_unlock(&globGtp.gtp_lock);
Gtpv1uExtHeaderT *opt_ext = NULL;
Gtpv1uExtHeaderT ext = {0};
int extension_count = 0;
gtpu_extension_header_t ext = {};
if (bearer.outgoing_qfi != -1) {
opt_ext = &ext;
ext.ExtHeaderLen = 1; // in quad bytes EXT_HDR_LNTH_OCTET_UNITS
ext.pdusession_cntr.spare = 0;
ext.pdusession_cntr.PDU_type = UL_PDU_SESSION_INFORMATION;
ext.pdusession_cntr.QFI = bearer.outgoing_qfi;
ext.pdusession_cntr.Reflective_QoS_activation = false;
ext.pdusession_cntr.Paging_Policy_Indicator = false;
ext.NextExtHeaderType = NO_MORE_EXT_HDRS;
ext = {
.type = GTPU_EXT_UL_PDU_SESSION_INFORMATION,
.ul_pdu_session_information = {
.qmp = false,
.dl_delay_ind = false,
.ul_delay_ind = false,
.snp = false,
.n3n9_delay_ind = false,
.new_ie_flag = false,
.qfi = bearer.outgoing_qfi
}
};
extension_count++;
}
DevAssert(compatInst(instance) == bearer.sock_fd);
@@ -368,9 +371,30 @@ void gtpv1uSendDirect(instance_t instance,
len,
seqNumFlag,
npduNumFlag,
opt_ext ? PDU_SESSION_CONTAINER : NO_MORE_EXT_HDRS,
(uint8_t *)opt_ext,
sizeof(*opt_ext));
&ext,
extension_count);
}
static void fillDlDeliveryStatusReport(gtpu_extension_header_t *ext, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN)
{
*ext = {
.type = GTPU_EXT_DL_DATA_DELIVERY_STATUS,
.dl_data_delivery_status = {
/* previous version of the code was sending highest_transmitted_nr_pdcp_sn if
* it is != 0, let's do the same for the moment */
.highest_transmitted_nr_pdcp_sn_ind = NR_PDCP_PDU_SN != 0,
.highest_delivered_nr_pdcp_sn_ind = false,
.final_frame_ind = false,
.lost_packet_report = false,
.delivered_nr_pdcp_sn_range_ind = false,
.data_rate_ind = false,
.retransmitted_nr_pdcp_sn_ind = false,
.delivered_retransmitted_nr_pdcp_ind = false,
.cause_report = false,
.desired_buffer_size = RLC_buffer_availability,
.highest_transmitted_nr_pdcp_sn = NR_PDCP_PDU_SN
}
};
}
static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req)
@@ -950,7 +974,6 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const
sizeof recovery,
true,
false,
NO_MORE_EXT_HDRS,
NULL,
0);
}
@@ -1168,37 +1191,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st
LOG_D(GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN);
int rlc_tx_buffer_space = nr_rlc_get_available_tx_space(ctxt.rntiMaybeUEid, rb_id + 3);
LOG_D(GTPU, "Available buffer size in RLC for Tx: %d \n", rlc_tx_buffer_space);
/*Total size of DDD_status PDU = 1 octet to report extension header length
* size of mandatory part + 3 octets for highest transmitted/delivered PDCP SN
* 1 octet for padding + 1 octet for next extension header type,
* according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/
extensionHeader_t *extensionHeader;
extensionHeader = (extensionHeader_t *)calloc(1, sizeof(extensionHeader_t));
extensionHeader->buffer[0] = (1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1) / 4;
DlDataDeliveryStatus_flagsT DlDataDeliveryStatus;
DlDataDeliveryStatus.deliveredPdcpSn = 0;
DlDataDeliveryStatus.transmittedPdcpSn = 1;
DlDataDeliveryStatus.pduType = 1;
DlDataDeliveryStatus.drbBufferSize =
htonl(rlc_tx_buffer_space); // htonl(10000000); //hardcoded for now but normally we should extract it from RLC
memcpy(extensionHeader->buffer + 1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT));
uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT) + 1;
extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff;
extensionHeader->buffer[offset + 1] = (NR_PDCP_PDU_SN >> 8) & 0xff;
extensionHeader->buffer[offset + 2] = NR_PDCP_PDU_SN & 0xff;
LOG_D(GTPU,
"Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n",
extensionHeader->buffer[offset],
extensionHeader->buffer[offset + 1],
extensionHeader->buffer[offset + 2]);
extensionHeader->buffer[offset + 3] = 0x00; // Padding octet
extensionHeader->buffer[offset + 4] = 0x00; // No more extension headers
/*Total size of DDD_status PDU = size of mandatory part +
* 3 octets for highest transmitted/delivered PDCP SN +
* 1 octet for padding + 1 octet for next extension header type,
* according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/
extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1;
gtpu_extension_header_t ext;
fillDlDeliveryStatusReport(&ext, rlc_tx_buffer_space, NR_PDCP_PDU_SN);
uint32_t teid = globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid;
gtpv1u_bearer_t bearer = create_bearer(h, addr, teid, 0);
gtpv1uCreateAndSendMsg(&bearer,
@@ -1207,9 +1201,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st
0,
false,
false,
NR_RAN_CONTAINER,
extensionHeader->buffer,
extensionHeader->length);
&ext,
1);
}
LOG_D(GTPU, "[%d] Received a %d bytes packet for: teid:%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid));
+137
View File
@@ -0,0 +1,137 @@
/*
* 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 "gtpu_extensions.h"
#include "common/utils/assertions.h"
#include "common/utils/ds/byte_array_producer.h"
#include "LOG/log.h"
/* 29.281 Figure 5.2.1-3 */
#define NR_RAN_CONTAINER 0x84
#define PDU_SESSION_CONTAINER 0x85
/* from an extension type, returns its "extension header type"
* as defined in 29.281 Figure 5.2.1-3
*/
int serialize_gtpu_extension_type(gtpu_extension_header_type_t type)
{
switch (type) {
case GTPU_EXT_NONE:
/* 0, no more extension */
return 0;
case GTPU_EXT_UL_PDU_SESSION_INFORMATION:
return PDU_SESSION_CONTAINER;
case GTPU_EXT_DL_DATA_DELIVERY_STATUS:
return NR_RAN_CONTAINER;
default:
AssertFatal(0, "unknown GTPU extension type %d\n", type);
}
}
/* returns 0 on error, 1 on success */
static int serialize_ul_pdu_session_information(byte_array_producer_t *b, ul_pdu_session_information_t *ext)
{
/* see 38.415 5.5.2.2 */
AssertFatal(!ext->qmp && !ext->dl_delay_ind && !ext->ul_delay_ind
&& !ext->snp && !ext->n3n9_delay_ind && !ext->new_ie_flag,
"todo\n");
uint8_t b1 = (1 << 4) | (ext->qmp << 3) | (ext->dl_delay_ind << 2) | (ext->ul_delay_ind << 1) | ext->snp;
uint8_t b2 = (ext->n3n9_delay_ind << 7) | (ext->new_ie_flag << 6) | ext->qfi;
return byte_array_producer_put_byte(b, b1) && byte_array_producer_put_byte(b, b2);
}
/* returns 0 on error, 1 on success */
static int serialize_dl_data_delivery_status(byte_array_producer_t *b, dl_data_delivery_status_t *ext)
{
/* see 38.425 5.5.2.2 */
AssertFatal(!ext->highest_delivered_nr_pdcp_sn_ind && !ext->final_frame_ind
&& !ext->lost_packet_report && !ext->delivered_nr_pdcp_sn_range_ind
&& !ext->data_rate_ind && !ext->retransmitted_nr_pdcp_sn_ind
&& !ext->delivered_retransmitted_nr_pdcp_ind && !ext->cause_report,
"todo\n");
uint8_t b1 = (1 << 4)
| (ext->highest_transmitted_nr_pdcp_sn_ind << 3)
| (ext->highest_delivered_nr_pdcp_sn_ind << 2)
| (ext->final_frame_ind << 1)
| ext->lost_packet_report;
uint8_t b2 = (ext->delivered_nr_pdcp_sn_range_ind << 4)
| (ext->data_rate_ind << 3)
| (ext->retransmitted_nr_pdcp_sn_ind << 2)
| (ext->delivered_retransmitted_nr_pdcp_ind << 1)
| ext->cause_report;
if (!byte_array_producer_put_byte(b, b1) || !byte_array_producer_put_byte(b, b2))
return 0;
if (!byte_array_producer_put_u32_be(b, ext->desired_buffer_size))
return 0;
if (ext->highest_transmitted_nr_pdcp_sn_ind)
if (!byte_array_producer_put_u24_be(b, ext->highest_transmitted_nr_pdcp_sn))
return 0;
return 1;
}
/* returns -1 on error, number of serialized bytes on success */
int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type_t next, uint8_t *out_buf, int out_len)
{
byte_array_producer_t b;
b = byte_array_producer_from_buffer(out_buf, out_len);
/* length - will be set later */
if (!byte_array_producer_put_byte(&b, 0))
goto error;
switch (ext->type) {
case GTPU_EXT_UL_PDU_SESSION_INFORMATION:
if (!serialize_ul_pdu_session_information(&b, &ext->ul_pdu_session_information))
goto error;
break;
case GTPU_EXT_DL_DATA_DELIVERY_STATUS:
if (!serialize_dl_data_delivery_status(&b, &ext->dl_data_delivery_status))
goto error;
break;
default:
LOG_E(GTPU, "unknown extension type %d\n", ext->type);
return -1;
}
/* padding */
while ((b.pos & 3) != 3)
if (!byte_array_producer_put_byte(&b, 0))
goto error;
/* next */
if (!byte_array_producer_put_byte(&b, serialize_gtpu_extension_type(next)))
goto error;
/* length is now know */
DevAssert(b.pos / 4 <= 255);
out_buf[0] = b.pos / 4;
return b.pos;
error:
LOG_E(GTPU, "error serializing extension\n");
return -1;
}
+75
View File
@@ -0,0 +1,75 @@
/*
* 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 GTPU_EXTENSIONS_H
#define GTPU_EXTENSIONS_H
#include <stdint.h>
#include <stdbool.h>
typedef enum {
GTPU_EXT_NONE,
/* 38.415 */
GTPU_EXT_UL_PDU_SESSION_INFORMATION,
/* 38.425 */
GTPU_EXT_DL_DATA_DELIVERY_STATUS,
} gtpu_extension_header_type_t;
/* 38.415 */
typedef struct {
/* not all fields are present, to be refined if needed */
bool qmp;
bool dl_delay_ind;
bool ul_delay_ind;
bool snp;
bool n3n9_delay_ind;
bool new_ie_flag;
int qfi;
} ul_pdu_session_information_t;
/* 38.425 */
typedef struct {
/* not all fields are present, to be refined if needed */
bool highest_transmitted_nr_pdcp_sn_ind;
bool highest_delivered_nr_pdcp_sn_ind;
bool final_frame_ind;
bool lost_packet_report;
bool delivered_nr_pdcp_sn_range_ind;
bool data_rate_ind;
bool retransmitted_nr_pdcp_sn_ind;
bool delivered_retransmitted_nr_pdcp_ind;
bool cause_report;
uint32_t desired_buffer_size;
uint32_t highest_transmitted_nr_pdcp_sn;
} dl_data_delivery_status_t;
typedef struct {
gtpu_extension_header_type_t type;
union {
ul_pdu_session_information_t ul_pdu_session_information;
dl_data_delivery_status_t dl_data_delivery_status;
};
} gtpu_extension_header_t;
int serialize_gtpu_extension_type(gtpu_extension_header_type_t type);
int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type_t next, uint8_t *out_buf, int out_len);
#endif /* GTPU_EXTENSIONS_H */