Merge remote-tracking branch 'origin/interop-srsran-du' into integration_2025_w34 (!3550)
some work for better interoperability with srsRAN DU
Just add the minimum to get some user plane traffic going on.
When doing user plane traffic, in the CU logs we have a lot of:
[GTPU] NR-RAN container type: 1 not supported
That will be fixed in another MR, not here.
This commit is contained in:
+2
-1
@@ -1396,9 +1396,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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
add_library(ds OBJECT
|
||||
byte_array.c
|
||||
byte_array_producer.c
|
||||
seq_arr.c
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 "byte_array_producer.h"
|
||||
|
||||
/* create a byte array producer from an existing buffer */
|
||||
byte_array_producer_t byte_array_producer_from_buffer(uint8_t *buffer, int len)
|
||||
{
|
||||
byte_array_producer_t result = { .b.len = len, .b.buf = buffer, .pos = 0 };
|
||||
return result;
|
||||
}
|
||||
|
||||
/* put a byte in a byte array if there is space
|
||||
* returns 1 on success, 0 on error
|
||||
*/
|
||||
int byte_array_producer_put_byte(byte_array_producer_t *b, uint8_t byte)
|
||||
{
|
||||
if (b->pos == b->b.len)
|
||||
return 0;
|
||||
|
||||
b->b.buf[b->pos] = byte;
|
||||
b->pos++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* put an u32 number as big endian in a byte array if there is space
|
||||
* returns 1 on success, 0 on error
|
||||
*/
|
||||
int byte_array_producer_put_u32_be(byte_array_producer_t *b, uint32_t v)
|
||||
{
|
||||
return byte_array_producer_put_byte(b, (v >> 24) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, (v >> 16) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, (v >> 8) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, v & 0xff);
|
||||
}
|
||||
|
||||
/* put an u24 number as big endian in a byte array if there is space
|
||||
* returns 1 on success, 0 on error
|
||||
*/
|
||||
int byte_array_producer_put_u24_be(byte_array_producer_t *b, uint32_t v)
|
||||
{
|
||||
return byte_array_producer_put_byte(b, (v >> 16) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, (v >> 8) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, v & 0xff);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 BYTE_ARRAY_PRODUCER_H
|
||||
#define BYTE_ARRAY_PRODUCER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "byte_array.h"
|
||||
|
||||
typedef struct {
|
||||
byte_array_t b;
|
||||
size_t pos;
|
||||
} byte_array_producer_t;
|
||||
|
||||
byte_array_producer_t byte_array_producer_from_buffer(uint8_t *buffer, int len);
|
||||
|
||||
int byte_array_producer_put_byte(byte_array_producer_t *b, uint8_t v);
|
||||
int byte_array_producer_put_u32_be(byte_array_producer_t *b, uint32_t v);
|
||||
int byte_array_producer_put_u24_be(byte_array_producer_t *b, uint32_t v);
|
||||
|
||||
#endif
|
||||
@@ -469,7 +469,7 @@ static void deliver_pdu_drb_gnb(void *deliver_pdu_data, ue_id_t ue_id, int rb_id
|
||||
if (NODE_IS_CU(node_type)) {
|
||||
LOG_D(PDCP, "%s() (drb %d) sending message to gtp size %d\n", __func__, rb_id, size);
|
||||
extern instance_t CUuniqInstance;
|
||||
gtpv1uSendDirect(CUuniqInstance, ue_id, rb_id, (uint8_t *)buf, size, false, false);
|
||||
gtpv1uSendDirectWithNRUSeqNum(CUuniqInstance, ue_id, rb_id, (uint8_t *)buf, size);
|
||||
} else {
|
||||
uint8_t *memblock = malloc16(size);
|
||||
memcpy(memblock, buf, size);
|
||||
|
||||
@@ -2305,13 +2305,19 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i
|
||||
uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&cellGroupConfig, (uint8_t *)cgc->buf, cgc->len);
|
||||
AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
|
||||
|
||||
if (UE->masterCellGroup) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
|
||||
LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
|
||||
/* hack for interoperability with srsRAN: ignore cell group config if empty */
|
||||
/* cell group config is empty if buffer length is 2 and content is all 0 */
|
||||
bool cgc_is_empty = cgc->len == 2 && cgc->buf[0] == 0 && cgc->buf[1] == 0;
|
||||
if (!cgc_is_empty) {
|
||||
if (UE->masterCellGroup) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
|
||||
LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
|
||||
}
|
||||
UE->masterCellGroup = cellGroupConfig;
|
||||
rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE);
|
||||
} else {
|
||||
LOG_W(NR_RRC, "hack: UE %d: ignore empty CellGroupConfig in UEContextModificationResponse\n", UE->rrc_ue_id);
|
||||
}
|
||||
UE->masterCellGroup = cellGroupConfig;
|
||||
|
||||
rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE);
|
||||
}
|
||||
|
||||
// Reconfiguration should have been sent to the UE, so it will attempt the
|
||||
|
||||
+143
-75
@@ -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)
|
||||
@@ -112,6 +113,7 @@ typedef struct gtpv1u_bearer_s {
|
||||
teid_t teid_outgoing;
|
||||
uint16_t seqNum;
|
||||
uint8_t npduNum;
|
||||
int32_t nru_sequence_number;
|
||||
int outgoing_qfi;
|
||||
} gtpv1u_bearer_t;
|
||||
|
||||
@@ -230,11 +232,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 +242,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 +252,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;
|
||||
}
|
||||
@@ -307,13 +307,14 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer,
|
||||
return !GTPNOK;
|
||||
}
|
||||
|
||||
void gtpv1uSendDirect(instance_t instance,
|
||||
ue_id_t ue_id,
|
||||
int bearer_id,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
bool seqNumFlag,
|
||||
bool npduNumFlag)
|
||||
static void _gtpv1uSendDirect(instance_t instance,
|
||||
ue_id_t ue_id,
|
||||
int bearer_id,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
bool seqNumFlag,
|
||||
bool npduNumFlag,
|
||||
int32_t nru_seqnum)
|
||||
{
|
||||
pthread_mutex_lock(&globGtp.gtp_lock);
|
||||
getInstRetVoid(compatInst(instance));
|
||||
@@ -347,18 +348,44 @@ 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[2];
|
||||
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;
|
||||
/* 29.281 Figure 5.2.1-3 note 4 says PDU Session Container must come first.
|
||||
* GTPU_EXT_UL_PDU_SESSION_INFORMATION is within a PDU Session Container
|
||||
* so it must be put before any other extension.
|
||||
*/
|
||||
ext[extension_count] = {
|
||||
.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++;
|
||||
}
|
||||
|
||||
if (nru_seqnum != -1) {
|
||||
ext[extension_count] = {
|
||||
.type = GTPU_EXT_DL_USER_DATA,
|
||||
.dl_user_data = {
|
||||
.dl_discard_blocks = false,
|
||||
.dl_flush = false,
|
||||
.report_polling = false,
|
||||
.request_out_of_seq_report = false,
|
||||
.report_delivered = false,
|
||||
.user_data_existence_flag = false,
|
||||
.assistance_info_report_polling_flag = false,
|
||||
.retransmission_flag = false,
|
||||
.nru_sequence_number = (uint32_t)nru_seqnum
|
||||
}
|
||||
};
|
||||
extension_count++;
|
||||
}
|
||||
|
||||
DevAssert(compatInst(instance) == bearer.sock_fd);
|
||||
@@ -368,9 +395,81 @@ 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);
|
||||
}
|
||||
|
||||
void gtpv1uSendDirect(instance_t instance,
|
||||
ue_id_t ue_id,
|
||||
int bearer_id,
|
||||
uint8_t *buf,
|
||||
size_t len,
|
||||
bool seqNumFlag,
|
||||
bool npduNumFlag)
|
||||
{
|
||||
_gtpv1uSendDirect(instance,
|
||||
ue_id,
|
||||
bearer_id,
|
||||
buf,
|
||||
len,
|
||||
seqNumFlag,
|
||||
npduNumFlag,
|
||||
-1);
|
||||
}
|
||||
|
||||
void gtpv1uSendDirectWithNRUSeqNum(instance_t instance,
|
||||
ue_id_t ue_id,
|
||||
int bearer_id,
|
||||
uint8_t *buf,
|
||||
size_t len)
|
||||
{
|
||||
pthread_mutex_lock(&globGtp.gtp_lock);
|
||||
getInstRetVoid(compatInst(instance));
|
||||
getUeRetVoid(inst, ue_id);
|
||||
auto ptr2 = ptrUe->second.bearers.find(bearer_id);
|
||||
|
||||
if (ptr2 == ptrUe->second.bearers.end()) {
|
||||
LOG_E(GTPU, "[%ld] GTP-U instance: sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id);
|
||||
pthread_mutex_unlock(&globGtp.gtp_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t nru_seqnum = ptr2->second.nru_sequence_number;
|
||||
ptr2->second.nru_sequence_number++;
|
||||
ptr2->second.nru_sequence_number &= (1 << 24) - 1;
|
||||
|
||||
pthread_mutex_unlock(&globGtp.gtp_lock);
|
||||
|
||||
_gtpv1uSendDirect(instance,
|
||||
ue_id,
|
||||
bearer_id,
|
||||
buf,
|
||||
len,
|
||||
false,
|
||||
false,
|
||||
nru_seqnum);
|
||||
}
|
||||
|
||||
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 +1049,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 +1266,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 +1276,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));
|
||||
|
||||
@@ -128,6 +128,11 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t;
|
||||
int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id);
|
||||
|
||||
void gtpv1uSendDirect(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len, bool seqNumFlag, bool npduNumFlag);
|
||||
void gtpv1uSendDirectWithNRUSeqNum(instance_t instance,
|
||||
ue_id_t ue_id,
|
||||
int bearer_id,
|
||||
uint8_t *buf,
|
||||
size_t len);
|
||||
|
||||
instance_t gtpv1Init(openAddr_t context);
|
||||
void *gtpv1uTask(void *args);
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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:
|
||||
case GTPU_EXT_DL_USER_DATA:
|
||||
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 0 on error, 1 on success */
|
||||
static int serialize_dl_user_data(byte_array_producer_t *b, dl_user_data_t *ext)
|
||||
{
|
||||
/* see 38.425 5.5.2.1 */
|
||||
AssertFatal(!ext->dl_discard_blocks && !ext->dl_flush && !ext->report_polling
|
||||
&& !ext->request_out_of_seq_report && !ext->report_delivered
|
||||
&& !ext->user_data_existence_flag && !ext->assistance_info_report_polling_flag
|
||||
&& !ext->retransmission_flag,
|
||||
"todo\n");
|
||||
|
||||
uint8_t b1 = (0 << 4) | (ext->dl_discard_blocks << 2) | (ext->dl_flush << 1) | ext->report_polling;
|
||||
uint8_t b2 = (ext->request_out_of_seq_report << 4)
|
||||
| (ext->report_delivered << 3)
|
||||
| (ext->user_data_existence_flag << 2)
|
||||
| (ext->assistance_info_report_polling_flag << 1)
|
||||
| ext->retransmission_flag;
|
||||
if (!byte_array_producer_put_byte(b, b1) || !byte_array_producer_put_byte(b, b2))
|
||||
return 0;
|
||||
|
||||
if (!byte_array_producer_put_u24_be(b, ext->nru_sequence_number))
|
||||
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;
|
||||
case GTPU_EXT_DL_USER_DATA:
|
||||
if (!serialize_dl_user_data(&b, &ext->dl_user_data))
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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_EXT_DL_USER_DATA,
|
||||
} 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;
|
||||
|
||||
/* 38.425 */
|
||||
typedef struct {
|
||||
/* not all fields are present, to be refined if needed */
|
||||
bool dl_discard_blocks;
|
||||
bool dl_flush;
|
||||
bool report_polling;
|
||||
bool request_out_of_seq_report;
|
||||
bool report_delivered;
|
||||
bool user_data_existence_flag;
|
||||
bool assistance_info_report_polling_flag;
|
||||
bool retransmission_flag;
|
||||
uint32_t nru_sequence_number;
|
||||
} dl_user_data_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;
|
||||
dl_user_data_t dl_user_data;
|
||||
};
|
||||
} 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 */
|
||||
Reference in New Issue
Block a user