diff --git a/CMakeLists.txt b/CMakeLists.txt index 2544c86d7a..c4e2a429c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt index a5ae86c52f..b5f5efb47a 100644 --- a/common/utils/ds/CMakeLists.txt +++ b/common/utils/ds/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(ds OBJECT byte_array.c + byte_array_producer.c seq_arr.c ) diff --git a/common/utils/ds/byte_array_producer.c b/common/utils/ds/byte_array_producer.c new file mode 100644 index 0000000000..07f46c363c --- /dev/null +++ b/common/utils/ds/byte_array_producer.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); +} diff --git a/common/utils/ds/byte_array_producer.h b/common/utils/ds/byte_array_producer.h new file mode 100644 index 0000000000..2087c77af1 --- /dev/null +++ b/common/utils/ds/byte_array_producer.h @@ -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 + +#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 diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index e170e90823..4d1c0b5ba0 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -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); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 75f6838a78..000c4f46cb 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -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 diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 9e07a44747..9be1873612 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -21,6 +21,7 @@ extern "C" { #include #include #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)); diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index d341acb311..4fcb8190e4 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -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); diff --git a/openair3/ocp-gtpu/gtpu_extensions.c b/openair3/ocp-gtpu/gtpu_extensions.c new file mode 100644 index 0000000000..13f0cee053 --- /dev/null +++ b/openair3/ocp-gtpu/gtpu_extensions.c @@ -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; +} diff --git a/openair3/ocp-gtpu/gtpu_extensions.h b/openair3/ocp-gtpu/gtpu_extensions.h new file mode 100644 index 0000000000..b621d27e14 --- /dev/null +++ b/openair3/ocp-gtpu/gtpu_extensions.h @@ -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 +#include + +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 */