Commit 5aba7ac9 authored by Guido Casati's avatar Guido Casati Committed by Guido Casati

Handling setup of multiple DRBs with different QoS flows in GTP-U

* current assumption: multiple DRBs, 1 QoS flow per DRB

Set QFI in GTP-U data request in a multi-DRB with multi-QoS scenario:

* each GTP-U packet processed within the GTP-U tunnel
  and which belongs to a QoS Flow mapped to a DRB
  has to be marked with the corresponding QFI
* Set QFI in GTP-U data request
* hardcoded to -1 in F1 interface, since it is not relevant
parent 84f6c0a5
......@@ -118,6 +118,7 @@ typedef struct gtpv1u_tunnel_data_req_s {
uint32_t offset; ///< start of message offset in buffer
ue_id_t ue_id;
rb_id_t bearer_id;
int qfi;
} gtpv1u_tunnel_data_req_t;
typedef struct gtpv1u_enb_data_forwarding_req_s {
......@@ -171,6 +172,16 @@ typedef struct {
char localPortStr[256];
} Gtpv1uReq;
/**
* @brief QoS Flow information for a given bearer
*/
typedef struct gtpv1u_qos_s {
/* QFIs list for each Bearer (0 ... Max Number of QFIs per Bearer) */
int qfi[NR_GTPV1U_MAX_QFI_PER_BEARER];
/* Bearer ID */
int rb_id;
} gtpv1u_qos_t;
/**
* @brief GTP-U Create Tunnel Request
*/
......@@ -178,8 +189,8 @@ typedef struct gtpv1u_gnb_create_tunnel_req_s {
ue_id_t ue_id;
/* Outgoing TEID for the GTP tunnel request */
teid_t outgoing_teid;
/* Outgoing QFIs for each Bearer within the tunnel (assuming 1 QoS Flow only) */
int outgoing_qfi;
/* Outgoing QFIs for each Bearer within the tunnel (0 ... Max Number of Bearers per PDU Session) */
gtpv1u_qos_t outgoing_qfi[NR_GTPV1U_MAX_BEARERS_PER_UE];
/* PDU Session ID for the GTP tunnel request */
pdusessionid_t pdusession_id;
/* Incoming RB for the GTP tunnel request */
......
......@@ -54,15 +54,14 @@ static int drb_gtpu_create(instance_t instance,
gtpCallback callBack,
gtpv1u_gnb_create_tunnel_resp_t *resp_f1)
{
const int bearer_id = 0;
int qfi = -1; // don't put PDU session marker in GTP for F1-U
gtpv1u_gnb_create_tunnel_req_t create_tunnel_req = {
.ue_id = ue_id,
.outgoing_teid = drb->up_ul_tnl[0].teid,
.outgoing_qfi = qfi,
.pdusession_id = drb->drb_id,
.incoming_rb_id = drb->drb_id,
.dst_addr.length = 32
};
gtpv1u_gnb_create_tunnel_req_t create_tunnel_req = {.ue_id = ue_id,
.outgoing_teid = drb->up_ul_tnl[0].teid,
.outgoing_qfi[bearer_id].qfi[0] = qfi,
.pdusession_id = drb->drb_id,
.incoming_rb_id = drb->drb_id,
.dst_addr.length = 32};
memcpy(&create_tunnel_req.dst_addr.buffer, &drb->up_ul_tnl[0].tl_address, sizeof(uint8_t) * 4); // only IPv4 now
LOG_D(E1AP, "Incoming RB %ld / Outgoing RB %ld - UL TEID %d QFI %d\n", drb->drb_id, drb->drb_id, drb->up_ul_tnl[0].teid, qfi);
// we use gtpv1u_create_ngu_tunnel because it returns the interface
......
......@@ -98,14 +98,15 @@ static int drb_gtpu_create(instance_t instance,
teid_t dummy_teid = 0xffff; // we will update later with answer from DU
in_addr_t dummy_address = {0}; // IPv4, updated later with answer from DU
gtpCallback callBack = cu_f1u_data_req;
const int bearer_id = 0; // 1 DRB
int qfi = -1; // don't put PDU session marker in GTP for F1-U
gtpv1u_gnb_create_tunnel_req_t create_tunnel_req = {
.ue_id = ue_id,
.outgoing_teid = dummy_teid,
.outgoing_qfi = qfi,
.pdusession_id = drb->id,
.incoming_rb_id = drb->id,
.dst_addr.length = 32,
.ue_id = ue_id,
.outgoing_teid = dummy_teid,
.outgoing_qfi[bearer_id].qfi[0] = qfi,
.pdusession_id = drb->id,
.incoming_rb_id = drb->id,
.dst_addr.length = 32,
};
memcpy(&create_tunnel_req.dst_addr.buffer, &dummy_address, sizeof(uint8_t) * 4);
LOG_D(E1AP, "In %s: Incoming RB ID %ld Outgoing RB ID %ld - UL TEID (dummy) %d QFI %d\n", __func__, drb->id, drb->id, dummy_teid, qfi);
......@@ -131,20 +132,25 @@ static void fill_gtpu_n3_req(gtpv1u_gnb_create_tunnel_req_t *req_n3, const pdu_s
}
/**
* @brief Loop though the number of QoS Flows to setup per DRB
* @brief Loop though the number of QoS Flows to setup per DRB and return DRB-to-QoS mapping
*/
static int process_qosflowSetup(DRB_nGRAN_setup_t *resp_drb, const DRB_nGRAN_to_setup_t *req_drb)
static gtpv1u_qos_t process_qosflowSetup(DRB_nGRAN_setup_t *resp_drb, const DRB_nGRAN_to_setup_t *req_drb)
{
int outgoing_qfi;
resp_drb->numQosFlowSetup = 1;
/* Loop though the number of QoS Flows to setup
* NOTE (A) only 1 QFI per DRB */
for (int k = 0; k < resp_drb->numQosFlowSetup; k++) {
gtpv1u_qos_t outgoing_qfi;
int k;
resp_drb->numQosFlowSetup = req_drb->numQosFlow2Setup;
/* Loop though the number of QoS Flows to setup for current DRB */
outgoing_qfi.rb_id = resp_drb->id;
for (k = 0; k < resp_drb->numQosFlowSetup; k++) {
const qos_flow_to_setup_t *qosflow2Setup = &req_drb->qosFlows[k];
qos_flow_setup_t *qosflowSetup = &resp_drb->qosFlows[k];
qosflowSetup->qfi = qosflow2Setup->qfi;
outgoing_qfi = qosflowSetup->qfi;
LOG_D(E1AP, "DRB %ld with QFI %ld to setup\n", resp_drb->id, qosflow2Setup->qfi);
outgoing_qfi.qfi[k] = qosflowSetup->qfi;
LOG_D(E1AP, "DRB %d with QFI %ld to setup\n", outgoing_qfi.rb_id, qosflow2Setup->qfi);
}
/* Set remaining QFIs to -1 */
for (int r = k + 1; r < SDAP_MAX_QFI; r++) {
outgoing_qfi.qfi[r] = -1;
}
return outgoing_qfi;
}
......@@ -209,7 +215,7 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req)
DRB_nGRAN_setup_t *resp_drb = &resp_pdu->DRBnGRanList[d];
resp_drb->id = req_drb->id;
/* Fill PDU session markers */
req_n3.outgoing_qfi = process_qosflowSetup(resp_drb, req_drb);
req_n3.outgoing_qfi[d] = process_qosflowSetup(resp_drb, req_drb);
// create PDCP bearers. This will also create SDAP bearers
NR_DRB_ToAddModList_t DRB_configList = {0};
fill_DRB_configList_e1(&DRB_configList, req_pdu);
......
......@@ -730,6 +730,7 @@ static void deliver_pdu_drb_gnb(void *deliver_pdu_data, ue_id_t ue_id, int rb_id
req->offset = GTPU_HEADER_OVERHEAD_MAX;
req->ue_id = ue_id; // use CU UE ID as GTP will use that to look up TEID
req->bearer_id = rb_id;
req->qfi = -1;
LOG_D(PDCP, "%s() (drb %d) sending message to gtp size %d\n", __func__, rb_id, size);
extern instance_t CUuniqInstance;
itti_send_msg_to_task(TASK_GTPV1_U, CUuniqInstance, message_p);
......
......@@ -503,6 +503,7 @@ rb_found:
req->offset = 0;
req->ue_id = ue->ue_id;
req->bearer_id=rb_id;
req->qfi = -1;
LOG_D(RLC, "Received uplink user-plane traffic at RLC-DU to be sent to the CU, size %d \n", size);
extern instance_t DUuniqInstance;
itti_send_msg_to_task(TASK_GTPV1_U, DUuniqInstance, msg);
......
......@@ -396,18 +396,31 @@ void trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession
pdu->teId = session->gtp_teid;
memcpy(&pdu->tlAddress, session->upf_addr.buffer, 4); // Fixme: dirty IPv4 target
/* we assume for the moment one DRB per PDU session. Activate the bearer,
* and configure in RRC. */
int drb_id = get_next_available_drb_id(UE);
drb_t *rrc_drb = generateDRB(UE,
drb_id,
pduSession,
rrc->configuration.enable_sdap,
rrc->security.do_drb_integrity,
rrc->security.do_drb_ciphering);
pdu->numDRB2Setup = 1; // One DRB per PDU Session. TODO: Remove hardcoding
for (int j=0; j < pdu->numDRB2Setup; j++) {
/* we currently assume 1 QoS Flow per DRB, therefore we setup
* as many DRBs as the number of QoS Flows decoded from the
* PDU Session Resource Setup */
pdu->numDRB2Setup = session->nb_qos; // TODO: update when iplementing multiple QoS per DRB
/* Loop through the DRBs to setup */
for (int j = 0; j < pdu->numDRB2Setup; j++) {
int drb_id = get_next_available_drb_id(UE);
/* Fetch QoS Flow from the PDU Session QoS Flows list
* NOTE: the assumption is that each DRB is mapped to 1 QoS Flow only
* therefore the QoS flow list item pointer is shifted by j, which is
* the current DRB to setup */
pdusession_level_qos_parameter_t *qos_session = session->qos + j;
int qfi = qos_session->qfi;
int fiveQI = qos_session->fiveQI;
/* generate DRB */
LOG_I(NR_RRC, "Generating DRB %d for QFI %d 5QI %d\n", drb_id, qfi, fiveQI);
drb_t *rrc_drb = generateDRB(UE,
drb_id,
pduSession->param.pdusession_id,
rrc->configuration.enable_sdap,
rrc->security.do_drb_integrity,
rrc->security.do_drb_ciphering,
qfi,
fiveQI);
/* DRB to be setup */
DRB_nGRAN_to_setup_t *drb = pdu->DRBnGRanList + j;
drb->id = rrc_drb->drb_id;
......@@ -426,11 +439,10 @@ void trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession
cellGroup->id = 0; // MCG
}
/* Loop throught the number of QoS flows mapped to this DRB */
drb->numQosFlow2Setup = 1; // TODO: update when iplementing multiple QoS per DRB: the mapping should come from SDAP
drb->numQosFlow2Setup = 1; // NOTE: implementation assumption is 1 QoS Flow per 1 DRB
for (int k = 0; k < drb->numQosFlow2Setup; k++) {
qos_flow_to_setup_t *qos_flow = drb->qosFlows + k;
pdusession_level_qos_parameter_t *qos_session = session->qos + k;
/* QoS Characteristics */
qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics;
qos_flow->qfi = qos_session->qfi;
qos_char->qos_type = qos_session->fiveQI_type;
......
......@@ -85,11 +85,13 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue,
const rrc_pdu_session_param_t *pduSession,
bool enable_sdap,
int do_drb_integrity,
int do_drb_ciphering)
int do_drb_ciphering,
int qfi,
int fiveQI)
{
DevAssert(ue != NULL);
LOG_I(NR_RRC, "UE %d: configure DRB ID %d for PDU session ID %d\n", ue->rrc_ue_id, drb_id, pduSession->param.pdusession_id);
LOG_I(NR_RRC, "UE %d: configure DRB ID %d for PDU session ID %d\n", ue->rrc_ue_id, drb_id, pdusession_id);
drb_t *est_drb = &ue->established_drbs[drb_id - 1];
DevAssert(est_drb->status == DRB_INACTIVE);
......@@ -110,9 +112,9 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue,
est_drb->cnAssociation.sdap_config.sdap_HeaderDL = NR_SDAP_Config__sdap_HeaderDL_absent;
est_drb->cnAssociation.sdap_config.sdap_HeaderUL = NR_SDAP_Config__sdap_HeaderUL_absent;
}
for (int qos_flow_index = 0; qos_flow_index < pduSession->param.nb_qos; qos_flow_index++) {
est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = pduSession->param.qos[qos_flow_index].qfi;
if (pduSession->param.qos[qos_flow_index].fiveQI > 5)
for (int qos_flow_index = 0; qos_flow_index < 1; qos_flow_index++) { // NOTE: implementation assumption is 1 QoS Flow per 1 DRB
est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = qfi;
if (fiveQI > 5)
est_drb->status = DRB_ACTIVE_NONGBR;
else
est_drb->status = DRB_ACTIVE;
......
......@@ -56,7 +56,9 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue,
const rrc_pdu_session_param_t *pduSession,
bool enable_sdap,
int do_drb_integrity,
int do_drb_ciphering);
int do_drb_ciphering,
int qfi,
int fiveQI);
/// @brief return the next available (inactive) DRB ID of UE ue
uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue);
......
......@@ -77,14 +77,8 @@ void sdap_data_ind(rb_id_t pdcp_entity,
return;
}
sdap_entity->rx_entity(sdap_entity,
pdcp_entity,
is_gnb,
has_sdap_rx,
pdusession_id,
ue_id,
buf,
size);
int qfi = sdap_entity->drb2qfi_map(sdap_entity, pdcp_entity);
sdap_entity->rx_entity(sdap_entity, pdcp_entity, qfi, is_gnb, has_sdap_rx, pdusession_id, ue_id, buf, size);
}
void set_qfi_pduid(uint8_t qfi, uint8_t pduid){
......
......@@ -232,6 +232,7 @@ static bool nr_sdap_tx_entity(nr_sdap_entity_t *entity,
static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
rb_id_t pdcp_entity,
int qfi,
int is_gnb,
bool has_sdap_rx,
int pdusession_id,
......@@ -271,12 +272,13 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity,
gtpv1u_tunnel_data_req_t *req = &GTPV1U_TUNNEL_DATA_REQ(message_p);
uint8_t *gtpu_buffer_p = (uint8_t *) (req + 1);
memcpy(gtpu_buffer_p + GTPU_HEADER_OVERHEAD_MAX, buf + offset, size - offset);
req->buffer = gtpu_buffer_p;
req->length = size - offset;
req->offset = GTPU_HEADER_OVERHEAD_MAX;
req->ue_id = ue_id;
req->bearer_id = pdusession_id;
LOG_D(SDAP, "%s() sending message to gtp size %d\n", __func__, size-offset);
req->buffer = gtpu_buffer_p;
req->length = size - offset;
req->offset = GTPU_HEADER_OVERHEAD_MAX;
req->ue_id = ue_id;
req->bearer_id = pdusession_id;
req->qfi = qfi;
LOG_D(SDAP, "%s(): sending message to gtp size %d for QFI %d PDU SESSION %d \n", __func__, size - offset, qfi, pdusession_id);
// very very dirty hack gloabl var N3GTPUInst
itti_send_msg_to_task(TASK_GTPV1_U, *N3GTPUInst, message_p);
} else { //nrUE
......@@ -538,6 +540,7 @@ nr_sdap_entity_t *new_nr_sdap_entity(int is_gnb,
sdap_entity->qfi2drb_map_update = nr_sdap_qfi2drb_map_update;
sdap_entity->qfi2drb_map_delete = nr_sdap_qfi2drb_map_del;
sdap_entity->qfi2drb_map = nr_sdap_qfi2drb;
sdap_entity->drb2qfi_map = nr_sdap_drb2qfi;
/* update SDAP entity list pointers */
sdap_entity->next_entity = sdap_info.sdap_entity_llist;
sdap_info.sdap_entity_llist = sdap_entity;
......
......@@ -94,7 +94,7 @@ typedef struct nr_sdap_entity_s {
void (*qfi2drb_map_update)(struct nr_sdap_entity_s *entity, uint8_t qfi, rb_id_t drb, bool has_sdap_rx, bool has_sdap_tx);
void (*qfi2drb_map_delete)(struct nr_sdap_entity_s *entity, uint8_t qfi);
rb_id_t (*qfi2drb_map)(struct nr_sdap_entity_s *entity, uint8_t qfi);
int (*drb2qfi_map)(struct nr_sdap_entity_s *entity, rb_id_t drb_id);
nr_sdap_ul_hdr_t (*sdap_construct_ctrl_pdu)(uint8_t qfi);
rb_id_t (*sdap_map_ctrl_pdu)(struct nr_sdap_entity_s *entity, rb_id_t pdcp_entity, int map_type, uint8_t dl_qfi);
void (*sdap_submit_ctrl_pdu)(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu);
......@@ -115,6 +115,7 @@ typedef struct nr_sdap_entity_s {
void (*rx_entity)(struct nr_sdap_entity_s *entity,
rb_id_t pdcp_entity,
int qfi,
int is_gnb,
bool has_sdap_rx,
int pdusession_id,
......
......@@ -104,7 +104,7 @@ typedef struct gtpv1u_bearer_s {
tcp_udp_port_t outgoing_port;
uint16_t seqNum;
uint8_t npduNum;
int outgoing_qfi;
gtpv1u_qos_t outgoing_qfi[NR_GTPV1U_MAX_BEARERS_PER_UE];
} gtpv1u_bearer_t;
typedef struct {
......@@ -291,6 +291,7 @@ static void gtpv1uSend(instance_t instance, gtpv1u_tunnel_data_req_t *req, bool
size_t length=req->length;
ue_id_t ue_id=req->ue_id;
int bearer_id=req->bearer_id;
int qfi = req->qfi;
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetVoid(compatInst(instance));
getUeRetVoid(inst, ue_id);
......@@ -316,12 +317,27 @@ static void gtpv1uSend(instance_t instance, gtpv1u_tunnel_data_req_t *req, bool
gtpv1u_bearer_t tmp=ptr2->second;
pthread_mutex_unlock(&globGtp.gtp_lock);
if (tmp.outgoing_qfi != -1) {
int j;
rb_id_t rb_id;
/* Check whether the QFI in the data request
for this GTP-U packet is mapped to a bearer */
bool addExtHeader = false;
for (j = 0; j < NR_GTPV1U_MAX_BEARERS_PER_UE; j++) {
for (int i = 0; i < NR_GTPV1U_MAX_QFI_PER_BEARER; i++) {
if (tmp.outgoing_qfi[j].qfi[i] == qfi && tmp.outgoing_qfi[j].qfi[i] != -1) {
rb_id = tmp.outgoing_qfi[j].rb_id;
addExtHeader = true;
LOG_D(GTPU, "QoS Flow %d mapped to DRB %ld\n", qfi, rb_id);
}
}
}
if (addExtHeader) {
/* we add GTP-U extension header */
Gtpv1uExtHeaderT ext = { 0 };
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 = tmp.outgoing_qfi;
ext.pdusession_cntr.QFI = qfi;
ext.pdusession_cntr.Reflective_QoS_activation = false;
ext.pdusession_cntr.Paging_Policy_Indicator = false;
ext.NextExtHeaderType = NO_MORE_EXT_HDRS;
......@@ -555,11 +571,12 @@ teid_t newGtpuCreateTunnel(instance_t instance,
int incoming_bearer_id,
int outgoing_bearer_id,
teid_t outgoing_teid,
int outgoing_qfi,
const gtpv1u_qos_t outgoing_qfi[NR_GTPV1U_MAX_BEARERS_PER_UE],
transport_layer_addr_t remoteAddr,
int port,
gtpCallback callBack,
gtpCallbackSDAP callBackSDAP) {
gtpCallbackSDAP callBackSDAP)
{
pthread_mutex_lock(&globGtp.gtp_lock);
getInstRetInt(compatInst(instance));
auto it=inst->ue2te_mapping.find(ue_id);
......@@ -610,7 +627,7 @@ teid_t newGtpuCreateTunnel(instance_t instance,
tmp->teid_incoming = incoming_teid;
tmp->outgoing_port=port;
tmp->teid_outgoing= outgoing_teid;
tmp->outgoing_qfi=outgoing_qfi;
memcpy(tmp->outgoing_qfi, outgoing_qfi, sizeof(gtpv1u_qos_t) * NR_GTPV1U_MAX_BEARERS_PER_UE);
pthread_mutex_unlock(&globGtp.gtp_lock);
char ip4[INET_ADDRSTRLEN];
char ip6[INET6_ADDRSTRLEN];
......@@ -648,12 +665,18 @@ int gtpv1u_create_s1u_tunnel(instance_t instance,
"From legacy code not clear, seems impossible (bearer=%d)\n",
create_tunnel_req->eps_bearer_id[i]);
int incoming_rb_id=create_tunnel_req->eps_bearer_id[i]-4;
gtpv1u_qos_t outgoing_qfi[NR_GTPV1U_MAX_BEARERS_PER_UE];
for (int i = 0; i < NR_GTPV1U_MAX_BEARERS_PER_UE; ++i) {
for (int j = 0; j < NR_GTPV1U_MAX_QFI_PER_BEARER; ++j) {
outgoing_qfi[i].qfi[j] = -1; // no pdu session in 4G
}
}
teid_t teid = newGtpuCreateTunnel(compatInst(instance),
create_tunnel_req->rnti,
incoming_rb_id,
create_tunnel_req->eps_bearer_id[i],
create_tunnel_req->sgw_S1u_teid[i],
-1, // no pdu session in 4G
outgoing_qfi,
create_tunnel_req->sgw_addr[i],
dstport,
callBack,
......
......@@ -88,7 +88,7 @@ extern "C" {
int incoming_bearer_id,
int outgoing_rb_id,
teid_t teid,
int outgoing_qfi,
const gtpv1u_qos_t outgoing_qfi[NR_GTPV1U_MAX_BEARERS_PER_UE],
transport_layer_addr_t remoteAddr,
int port,
gtpCallback callBack,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment