Commit 2776ec78 authored by Raymond Knopp's avatar Raymond Knopp

Add initial support for DL Data Delivery status reporting over NR-User plane protocol in gtp-u

parent d4b566ce
...@@ -34,6 +34,22 @@ typedef struct Gtpv1uMsgHeader { ...@@ -34,6 +34,22 @@ typedef struct Gtpv1uMsgHeader {
teid_t teid; teid_t teid;
} __attribute__((packed)) Gtpv1uMsgHeaderT; } __attribute__((packed)) Gtpv1uMsgHeaderT;
//TS 38.425, Figure 5.5.2.2-1
typedef struct DlDataDeliveryStatus_flags {
uint8_t LPR:1; //Lost packet report
uint8_t FFI:1; //Final Frame Ind
uint8_t deliveredPdcpSn:1; //Highest Delivered NR PDCP SN Ind
uint8_t transmittedPdcpSn:1; //Highest Transmitted NR PDCP SN Ind
uint8_t pduType:4; //PDU type
uint8_t CR:1; //Cause Report
uint8_t deliveredReTxPdcpSn:1; //Delivered retransmitted NR PDCP SN Ind
uint8_t reTxPdcpSn:1; //Retransmitted NR PDCP SN Ind
uint8_t DRI:1; //Data Rate Indication
uint8_t deliveredPdcpSnRange:1; //Delivered NR PDCP SN Range Ind
uint8_t spare:3;
uint32_t drbBufferSize; //Desired buffer size for the data radio bearer
} __attribute__((packed)) DlDataDeliveryStatus_flagsT;
#pragma pack() #pragma pack()
// TS 29.060, table 7.1 defines the possible message types // TS 29.060, table 7.1 defines the possible message types
...@@ -59,12 +75,14 @@ typedef struct ocp_gtpv1u_bearer_s { ...@@ -59,12 +75,14 @@ typedef struct ocp_gtpv1u_bearer_s {
typedef struct { typedef struct {
map<int, ocp_gtpv1u_bearer_t> bearers; map<int, ocp_gtpv1u_bearer_t> bearers;
teid_t outgoing_teid;
} teidData_t; } teidData_t;
typedef struct { typedef struct {
rnti_t rnti; rnti_t rnti;
ebi_t incoming_rb_id; ebi_t incoming_rb_id;
gtpCallback callBack; gtpCallback callBack;
teid_t outgoing_teid;
} rntiData_t; } rntiData_t;
class gtpEndPoint { class gtpEndPoint {
...@@ -109,14 +127,16 @@ instance_t legacyInstanceMapping=0; ...@@ -109,14 +127,16 @@ instance_t legacyInstanceMapping=0;
static int gtpv1uCreateAndSendMsg(int h, uint32_t peerIp, uint16_t peerPort, int msgType, teid_t teid, uint8_t *Msg,int msgLen, static int gtpv1uCreateAndSendMsg(int h, uint32_t peerIp, uint16_t peerPort, int msgType, teid_t teid, uint8_t *Msg,int msgLen,
bool seqNumFlag, bool npduNumFlag, bool extHdrFlag, int seqNum, int npduNum, int extHdrType) { bool seqNumFlag, bool npduNumFlag, bool extHdrFlag, int seqNum, int npduNum, int extHdrType,
AssertFatal(extHdrFlag==false,"Not developped"); uint8_t *extensionHeader_buffer, uint8_t extensionHeader_length) {
LOG_D(GTPU, "Peer IP:%u peer port:%u outgoing teid:%u \n", peerIp, peerPort, teid);
//AssertFatal(extHdrFlag==false,"Not developped");
int headerAdditional=0; int headerAdditional=0;
if ( seqNumFlag || npduNumFlag || extHdrFlag) if ( seqNumFlag || npduNumFlag || extHdrFlag)
headerAdditional=4; headerAdditional=4;
int fullSize=GTPV1U_HEADER_SIZE+headerAdditional+msgLen; int fullSize=GTPV1U_HEADER_SIZE+headerAdditional+msgLen+extensionHeader_length;
uint8_t buffer[fullSize]; uint8_t buffer[fullSize];
Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)buffer ; Gtpv1uMsgHeaderT *msgHdr = (Gtpv1uMsgHeaderT *)buffer ;
// N should be 0 for us (it was used only in 2G and 3G) // N should be 0 for us (it was used only in 2G and 3G)
...@@ -128,20 +148,42 @@ instance_t legacyInstanceMapping=0; ...@@ -128,20 +148,42 @@ instance_t legacyInstanceMapping=0;
msgHdr->PT=1; msgHdr->PT=1;
msgHdr->version=1; msgHdr->version=1;
msgHdr->msgType=msgType; msgHdr->msgType=msgType;
msgHdr->msgLength=htons(msgLen); msgHdr->msgLength=htons(msgLen+extensionHeader_length);
if ( seqNumFlag || extHdrFlag || npduNumFlag) if ( seqNumFlag || extHdrFlag || npduNumFlag)
msgHdr->msgLength+=4; msgHdr->msgLength+=htons(4);
msgHdr->teid=htonl(teid); msgHdr->teid=htonl(teid);
if(seqNumFlag || extHdrFlag || npduNumFlag) { if(seqNumFlag || extHdrFlag || npduNumFlag) {
*((uint16_t *) (buffer+8)) = seqNumFlag ? htons(seqNum) : 0x0000; *((uint16_t *) (buffer+8)) = seqNumFlag ? seqNum : 0x0000;
*((uint8_t *) (buffer+10)) = npduNumFlag ? htons(npduNum) : 0x00; *((uint8_t *) (buffer+10)) = npduNumFlag ? npduNum : 0x00;
*((uint8_t *) (buffer+11)) = extHdrFlag ? htons(extHdrType) : 0x00; *((uint8_t *) (buffer+11)) = extHdrFlag ? extHdrType : 0x00;
/**(buffer+8) = seqNumFlag ? htons(seqNum) : 0x0000;
*(buffer+10) = npduNumFlag ? htons(npduNum) : 0x00;
*(buffer+11) = extHdrFlag ? htons(extHdrType) : 0x00;
*(buffer+11) = extHdrType;*/
}
if(extHdrFlag){
while (extHdrType){
if (extensionHeader_length > 0 && extHdrType == 0x84){
memcpy(buffer+GTPV1U_HEADER_SIZE+headerAdditional, extensionHeader_buffer, 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");
return GTPNOK;
}
}
}
if (Msg!= NULL){
memcpy(buffer+GTPV1U_HEADER_SIZE+headerAdditional+extensionHeader_length, Msg, msgLen);
} }
memcpy(buffer+GTPV1U_HEADER_SIZE+headerAdditional, Msg, msgLen);
// Fix me: add IPv6 support, using flag ipVersion // Fix me: add IPv6 support, using flag ipVersion
static struct sockaddr_in to= {0}; static struct sockaddr_in to= {0};
to.sin_family = AF_INET; to.sin_family = AF_INET;
...@@ -198,7 +240,7 @@ static void gtpv1uSend(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req, b ...@@ -198,7 +240,7 @@ static void gtpv1uSend(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req, b
tmp.outgoing_port, tmp.outgoing_port,
GTP_GPDU, GTP_GPDU,
tmp.teid_outgoing, tmp.teid_outgoing,
buffer, length, seqNumFlag, npduNumFlag, false, tmp.seqNum, tmp.npduNum, 0) ; buffer, length, seqNumFlag, npduNumFlag, false, tmp.seqNum, tmp.npduNum, 0, NULL, 0) ;
} }
static void gtpv1uSend2(instance_t instance, gtpv1u_gnb_tunnel_data_req_t *req, bool seqNumFlag, bool npduNumFlag) { static void gtpv1uSend2(instance_t instance, gtpv1u_gnb_tunnel_data_req_t *req, bool seqNumFlag, bool npduNumFlag) {
...@@ -241,7 +283,7 @@ static void gtpv1uSend2(instance_t instance, gtpv1u_gnb_tunnel_data_req_t *req, ...@@ -241,7 +283,7 @@ static void gtpv1uSend2(instance_t instance, gtpv1u_gnb_tunnel_data_req_t *req,
tmp.outgoing_port, tmp.outgoing_port,
GTP_GPDU, GTP_GPDU,
tmp.teid_outgoing, tmp.teid_outgoing,
buffer, length, seqNumFlag, npduNumFlag, false, tmp.seqNum, tmp.npduNum, 0) ; buffer, length, seqNumFlag, npduNumFlag, false, tmp.seqNum, tmp.npduNum, 0, NULL, 0) ;
} }
static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req) { static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_tunnel_data_req_t *req) {
...@@ -445,6 +487,8 @@ teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int incoming_bearer ...@@ -445,6 +487,8 @@ teid_t newGtpuCreateTunnel(instance_t instance, rnti_t rnti, int incoming_bearer
inst->te2ue_mapping[incoming_teid].incoming_rb_id= incoming_bearer_id; inst->te2ue_mapping[incoming_teid].incoming_rb_id= incoming_bearer_id;
inst->te2ue_mapping[incoming_teid].outgoing_teid= outgoing_teid;
inst->te2ue_mapping[incoming_teid].callBack=callBack; inst->te2ue_mapping[incoming_teid].callBack=callBack;
ocp_gtpv1u_bearer_t *tmp=&inst->ue2te_mapping[rnti].bearers[outgoing_bearer_id]; ocp_gtpv1u_bearer_t *tmp=&inst->ue2te_mapping[rnti].bearers[outgoing_bearer_id];
...@@ -664,7 +708,7 @@ static int Gtpv1uHandleEchoReq(int h, ...@@ -664,7 +708,7 @@ static int Gtpv1uHandleEchoReq(int h,
return gtpv1uCreateAndSendMsg(h, peerIp, peerPort, GTP_ECHO_RSP, ntohl(msgHdr->teid), return gtpv1uCreateAndSendMsg(h, peerIp, peerPort, GTP_ECHO_RSP, ntohl(msgHdr->teid),
recovery, sizeof recovery, recovery, sizeof recovery,
1, 0, 0, seq, 0, 0); 1, 0, 0, seq, 0, 0, NULL, 0);
} }
...@@ -787,9 +831,24 @@ static int Gtpv1uHandleGpdu(int h, ...@@ -787,9 +831,24 @@ static int Gtpv1uHandleGpdu(int h,
if( msgHdr->E || msgHdr->S ||msgHdr->PN) if( msgHdr->E || msgHdr->S ||msgHdr->PN)
offset+=4; offset+=4;
uint32_t NR_PDCP_PDU_SN = 0;
//Consider the length of 1 or more extension headers that can be present in the GTP-U PDU //Consider the length of 1 or more extension headers that can be present in the GTP-U PDU
if(msgHdr->E) { if(msgHdr->E) {
while (msgBuf[offset]!=0) { while (msgBuf[offset]!=0){
if(msgBuf[offset-1] == 0x84){ //Extension header corresponding to NR-RAN container type
uint8_t PDU_type = (msgBuf[offset+1]>>4) & 0x0f;
if (PDU_type == 0){ //DL USER Data Format
if((msgBuf[offset+2]>>2)& 0x3){ //"Report delivered" and "User data existence flags" enabled (TS 38.425, 5.4)
/*Store the NR PDCP PDU SN for which a delivery status report shall be generated once the
*PDU gets forwarded to the lower layers*/
NR_PDCP_PDU_SN = msgBuf[offset+3] << 16 | msgBuf[offset+4] << 8 | msgBuf[offset+5];
LOG_D(GTPU, " NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN);
}
}
else{
LOG_W(GTPU, "NR-RAN container type: %d not supported \n", PDU_type);
}
}
LOG_D(GTPU, "Extension header length in multiple of 4 bytes is: %d \n", msgBuf[offset]); LOG_D(GTPU, "Extension header length in multiple of 4 bytes is: %d \n", msgBuf[offset]);
offset+=msgBuf[offset]*4 -1; offset+=msgBuf[offset]*4 -1;
} }
...@@ -830,6 +889,41 @@ static int Gtpv1uHandleGpdu(int h, ...@@ -830,6 +889,41 @@ static int Gtpv1uHandleGpdu(int h,
&sourceL2Id, &sourceL2Id,
&destinationL2Id) ) &destinationL2Id) )
LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h); LOG_E(GTPU,"[%d] down layer refused incoming packet\n", h);
if(NR_PDCP_PDU_SN > 0 && NR_PDCP_PDU_SN %5 ==0){
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);
/*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(1000000); //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->buffer[offset]: %u, extensionHeader->buffer[offset+1]:%u, extensionHeader->buffer[offset+2]:%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;
gtpv1uCreateAndSendMsg(h,
peerIp,
peerPort,
GTP_GPDU,
inst->te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid,
NULL, 0, false, false, true, 0, 0, 0x84, extensionHeader->buffer, extensionHeader->length) ;
}
LOG_D(GTPU,"[%d] Received a %d bytes packet for: teid:%x\n", h, LOG_D(GTPU,"[%d] Received a %d bytes packet for: teid:%x\n", h,
msgBufLen-offset, msgBufLen-offset,
......
...@@ -41,6 +41,11 @@ typedef struct openAddr_s { ...@@ -41,6 +41,11 @@ typedef struct openAddr_s {
instance_t originInstance; instance_t originInstance;
} openAddr_t; } openAddr_t;
typedef struct extensionHeader_s{
uint8_t buffer[500];
uint8_t length;
}extensionHeader_t;
// the init function create a gtp instance and return the gtp instance id // the init function create a gtp instance and return the gtp instance id
// the parameter originInstance will be sent back in each message from gtp to the creator // the parameter originInstance will be sent back in each message from gtp to the creator
void ocp_gtpv1uReceiver(int h); void ocp_gtpv1uReceiver(int h);
......
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