Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-RAN
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenXG
OpenXG-RAN
Commits
b061b576
Commit
b061b576
authored
Feb 04, 2025
by
Guido Casati
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add Bearer Context Modification Request enc/dec lib
parent
f2b80ae3
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
643 additions
and
1 deletion
+643
-1
openair2/E1AP/lib/e1ap_bearer_context_management.c
openair2/E1AP/lib/e1ap_bearer_context_management.c
+631
-1
openair2/E1AP/lib/e1ap_bearer_context_management.h
openair2/E1AP/lib/e1ap_bearer_context_management.h
+6
-0
openair2/E1AP/lib/e1ap_lib_includes.h
openair2/E1AP/lib/e1ap_lib_includes.h
+6
-0
No files found.
openair2/E1AP/lib/e1ap_bearer_context_management.c
View file @
b061b576
...
...
@@ -179,7 +179,7 @@ static bool eq_pdcp_config(const bearer_context_pdcp_config_t *a, const bearer_c
/** @brief Encode SDAP Configuration IE */
static
E1AP_SDAP_Configuration_t
e1_encode_sdap_config
(
const
bearer_context_sdap_config_t
*
in
)
{
E1AP_SDAP_Configuration_t
out
;
E1AP_SDAP_Configuration_t
out
=
{
0
}
;
out
.
defaultDRB
=
in
->
defaultDRB
?
E1AP_DefaultDRB_true
:
E1AP_DefaultDRB_false
;
out
.
sDAP_Header_UL
=
in
->
sDAP_Header_UL
;
out
.
sDAP_Header_DL
=
in
->
sDAP_Header_DL
;
...
...
@@ -1050,3 +1050,633 @@ void free_e1ap_context_setup_response(const e1ap_bearer_setup_resp_t *msg)
{
// Do nothing (no dynamic allocation)
}
/* ============================================
* E1AP Bearer Context Modification Request
* ============================================ */
/** @brief Encode PDU Session Item to modify */
static
E1AP_PDU_Session_Resource_To_Modify_Item_t
e1_encode_pdu_session_to_mod_item
(
const
pdu_session_to_mod_t
*
in
)
{
E1AP_PDU_Session_Resource_To_Modify_Item_t
out
=
{
0
};
out
.
pDU_Session_ID
=
in
->
sessionId
;
for
(
const
DRB_nGRAN_to_mod_t
*
j
=
in
->
DRBnGRanModList
;
j
<
in
->
DRBnGRanModList
+
in
->
numDRB2Modify
;
j
++
)
{
asn1cCalloc
(
out
.
dRB_To_Modify_List_NG_RAN
,
drb2Mod_List
);
asn1cSequenceAdd
(
drb2Mod_List
->
list
,
E1AP_DRB_To_Modify_Item_NG_RAN_t
,
drb2Mod
);
drb2Mod
->
dRB_ID
=
j
->
id
;
// PDCP Config (O)
if
(
j
->
pdcp_config
)
{
asn1cCalloc
(
drb2Mod
->
pDCP_Configuration
,
pDCP_Configuration
);
*
pDCP_Configuration
=
e1_encode_pdcp_config
(
j
->
pdcp_config
);
}
// SDAP Config (O)
if
(
j
->
sdap_config
)
{
asn1cCalloc
(
drb2Mod
->
sDAP_Configuration
,
sDAP_Configuration
);
*
sDAP_Configuration
=
e1_encode_sdap_config
(
j
->
sdap_config
);
}
// PDCP SN Status Request (O)
if
(
j
->
pdcp_sn_status_requested
)
{
asn1cCalloc
(
drb2Mod
->
pDCP_SN_Status_Request
,
pDCP_SN_Status_Request
);
*
pDCP_SN_Status_Request
=
E1AP_PDCP_SN_Status_Request_requested
;
}
// DL UP TNL parameters (O)
if
(
j
->
numDlUpParam
>
0
)
{
asn1cCalloc
(
drb2Mod
->
dL_UP_Parameters
,
DL_UP_Param_List
);
for
(
const
up_params_t
*
k
=
j
->
DlUpParamList
;
k
<
j
->
DlUpParamList
+
j
->
numDlUpParam
;
k
++
)
{
asn1cSequenceAdd
(
DL_UP_Param_List
->
list
,
E1AP_UP_Parameters_Item_t
,
DL_UP_Param
);
DL_UP_Param
->
uP_TNL_Information
=
e1_encode_up_tnl_info
(
&
k
->
tl_info
);
DL_UP_Param
->
cell_Group_ID
=
k
->
cell_group_id
;
}
}
// QoS Flows to setup (O)
for
(
const
qos_flow_to_setup_t
*
k
=
j
->
qosFlows
;
k
<
j
->
qosFlows
+
j
->
numQosFlow2Setup
;
k
++
)
{
asn1cCalloc
(
drb2Mod
->
flow_Mapping_Information
,
flow_Mapping_Information
);
asn1cSequenceAdd
(
flow_Mapping_Information
->
list
,
E1AP_QoS_Flow_QoS_Parameter_Item_t
,
ie1_2
);
*
ie1_2
=
e1_encode_qos_flow_to_setup
(
k
);
}
}
return
out
;
}
/** @brief Common encoding for the NG-RAN System IE */
static
E1AP_NG_RAN_BearerContextModificationRequest_t
*
encode_ng_ran_to_mod
(
E1AP_BearerContextModificationRequestIEs_t
*
ie
)
{
ie
->
id
=
E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest
;
ie
->
criticality
=
E1AP_Criticality_reject
;
ie
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_System_BearerContextModificationRequest
;
E1AP_System_BearerContextModificationRequest_t
*
reqIE
=
&
ie
->
value
.
choice
.
System_BearerContextModificationRequest
;
reqIE
->
present
=
E1AP_System_BearerContextModificationRequest_PR_nG_RAN_BearerContextModificationRequest
;
E1AP_ProtocolIE_Container_4932P26_t
*
msgNGRAN_list
=
calloc_or_fail
(
1
,
sizeof
(
*
msgNGRAN_list
));
reqIE
->
choice
.
nG_RAN_BearerContextModificationRequest
=
(
struct
E1AP_ProtocolIE_Container
*
)
msgNGRAN_list
;
asn1cSequenceAdd
(
msgNGRAN_list
->
list
,
E1AP_NG_RAN_BearerContextModificationRequest_t
,
msgNGRAN
);
return
msgNGRAN
;
}
/** @brief Encode PDU Session Resource To Setup Modification List item */
static
E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t
e1_encode_pdu_session_to_setup_mod_item
(
const
pdu_session_to_setup_t
*
in
)
{
E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t
item
=
{
0
};
// PDU Session ID (M)
item
.
pDU_Session_ID
=
in
->
sessionId
;
// PDU Session Type (M)
item
.
pDU_Session_Type
=
in
->
sessionType
;
// SNSSAI (M)
item
.
sNSSAI
=
e1_encode_snssai
(
&
in
->
nssai
);
// Security indication (M)
item
.
securityIndication
=
e1_encode_security_indication
(
&
in
->
securityIndication
);
// NG UL UP Transport Layer Information (M)
item
.
nG_UL_UP_TNL_Information
=
e1_encode_up_tnl_info
(
&
in
->
UP_TL_information
);
// DRB to setup
for
(
const
DRB_nGRAN_to_setup_t
*
j
=
in
->
DRBnGRanList
;
j
<
in
->
DRBnGRanList
+
in
->
numDRB2Setup
;
j
++
)
{
asn1cSequenceAdd
(
item
.
dRB_To_Setup_Mod_List_NG_RAN
.
list
,
E1AP_DRB_To_Setup_Mod_Item_NG_RAN_t
,
ie1
);
// DRB ID (M)
ie1
->
dRB_ID
=
j
->
id
;
// SDAP Config (M)
ie1
->
sDAP_Configuration
=
e1_encode_sdap_config
(
&
j
->
sdap_config
);
// PDCP Config (M)
ie1
->
pDCP_Configuration
=
e1_encode_pdcp_config
(
&
j
->
pdcp_config
);
// Cell Group Information (M)
for
(
const
cell_group_id_t
*
k
=
j
->
cellGroupList
;
k
<
j
->
cellGroupList
+
j
->
numCellGroups
;
k
++
)
{
asn1cSequenceAdd
(
ie1
->
cell_Group_Information
.
list
,
E1AP_Cell_Group_Information_Item_t
,
ie1_1
);
ie1_1
->
cell_Group_ID
=
*
k
;
}
// QoS Flows Information To Be Setup (M)
for
(
const
qos_flow_to_setup_t
*
k
=
j
->
qosFlows
;
k
<
j
->
qosFlows
+
j
->
numQosFlow2Setup
;
k
++
)
{
asn1cSequenceAdd
(
ie1
->
flow_Mapping_Information
.
list
,
E1AP_QoS_Flow_QoS_Parameter_Item_t
,
ie1_2
);
*
ie1_2
=
e1_encode_qos_flow_to_setup
(
k
);
}
}
return
item
;
}
/**
* @brief Encodes the E1 Bearer Modification Request message
*/
E1AP_E1AP_PDU_t
*
encode_E1_bearer_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
in
)
{
E1AP_E1AP_PDU_t
*
pdu
=
calloc_or_fail
(
1
,
sizeof
(
*
pdu
));
// PDU Type
pdu
->
present
=
E1AP_E1AP_PDU_PR_initiatingMessage
;
asn1cCalloc
(
pdu
->
choice
.
initiatingMessage
,
msg
);
msg
->
procedureCode
=
E1AP_ProcedureCode_id_bearerContextModification
;
msg
->
criticality
=
E1AP_Criticality_reject
;
msg
->
value
.
present
=
E1AP_InitiatingMessage__value_PR_BearerContextModificationRequest
;
E1AP_BearerContextModificationRequest_t
*
out
=
&
msg
->
value
.
choice
.
BearerContextModificationRequest
;
// gNB-CU-CP UE E1AP ID (M)
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie1
);
ie1
->
id
=
E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID
;
ie1
->
criticality
=
E1AP_Criticality_reject
;
ie1
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID
;
ie1
->
value
.
choice
.
GNB_CU_CP_UE_E1AP_ID
=
in
->
gNB_cu_cp_ue_id
;
// gNB-CU-UP UE E1AP ID (M)
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie2
);
ie2
->
id
=
E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID
;
ie2
->
criticality
=
E1AP_Criticality_reject
;
ie2
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_UP_UE_E1AP_ID
;
ie2
->
value
.
choice
.
GNB_CU_UP_UE_E1AP_ID
=
in
->
gNB_cu_up_ue_id
;
// Security Information (O)
if
(
in
->
secInfo
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie3
);
ie3
->
id
=
E1AP_ProtocolIE_ID_id_SecurityInformation
;
ie3
->
criticality
=
E1AP_Criticality_reject
;
ie3
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_SecurityInformation
;
ie3
->
value
.
choice
.
SecurityInformation
=
e1_encode_security_info
(
in
->
secInfo
);
}
// UE DL Aggregate Maximum Bit Rate (O)
if
(
in
->
ueDlAggMaxBitRate
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie4
);
ie4
->
id
=
E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate
;
ie4
->
criticality
=
E1AP_Criticality_reject
;
ie4
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_BitRate
;
INT32_TO_OCTET_STRING
(
*
in
->
ueDlAggMaxBitRate
,
&
ie4
->
value
.
choice
.
BitRate
);
}
// UE DL Maximum Integrity Protected Data Rate (O)
if
(
in
->
ueDlMaxIPBitRate
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie5
);
ie5
->
id
=
E1AP_ProtocolIE_ID_id_UEDLMaximumIntegrityProtectedDataRate
;
ie5
->
criticality
=
E1AP_Criticality_ignore
;
ie5
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_BitRate
;
INT32_TO_OCTET_STRING
(
*
in
->
ueDlMaxIPBitRate
,
&
ie5
->
value
.
choice
.
BitRate
);
}
// Bearer Context Status Change (O)
if
(
in
->
bearerContextStatus
&&
*
in
->
bearerContextStatus
==
BEARER_SUSPEND
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie6
);
ie6
->
id
=
E1AP_ProtocolIE_ID_id_BearerContextStatusChange
;
ie6
->
criticality
=
E1AP_Criticality_reject
;
ie6
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_BearerContextStatusChange
;
ie6
->
value
.
choice
.
BearerContextStatusChange
=
E1AP_BearerContextStatusChange_suspend
;
}
// UE Inactivity Timer (O)
if
(
in
->
inactivityTimer
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie7
);
ie7
->
id
=
E1AP_ProtocolIE_ID_id_UE_Inactivity_Timer
;
ie7
->
criticality
=
E1AP_Criticality_ignore
;
ie7
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_Inactivity_Timer
;
ie7
->
value
.
choice
.
Inactivity_Timer
=
*
in
->
inactivityTimer
;
}
// Data Discard Required (O)
if
(
in
->
dataDiscardRequired
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
ie9
);
ie9
->
id
=
E1AP_ProtocolIE_ID_id_DataDiscardRequired
;
ie9
->
criticality
=
E1AP_Criticality_ignore
;
ie9
->
value
.
present
=
E1AP_BearerContextModificationRequestIEs__value_PR_DataDiscardRequired
;
ie9
->
value
.
choice
.
DataDiscardRequired
=
in
->
dataDiscardRequired
;
}
// NG-RAN PDU Session Resource To Setup List (O)
if
(
in
->
numPDUSessions
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
setupList
);
E1AP_NG_RAN_BearerContextModificationRequest_t
*
msgNGRAN
=
encode_ng_ran_to_mod
(
setupList
);
msgNGRAN
->
id
=
E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_Mod_List
;
msgNGRAN
->
criticality
=
E1AP_Criticality_reject
;
msgNGRAN
->
value
.
present
=
E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Setup_Mod_List
;
E1AP_PDU_Session_Resource_To_Setup_Mod_List_t
*
pdu2Setup
=
&
msgNGRAN
->
value
.
choice
.
PDU_Session_Resource_To_Setup_Mod_List
;
for
(
int
i
=
0
;
i
<
in
->
numPDUSessions
;
i
++
)
{
const
pdu_session_to_setup_t
*
pdu_session
=
&
in
->
pduSession
[
i
];
asn1cSequenceAdd
(
pdu2Setup
->
list
,
E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t
,
item
);
*
item
=
e1_encode_pdu_session_to_setup_mod_item
(
pdu_session
);
}
}
// NG-RAN PDU Session Resource To Modify List (O)
if
(
in
->
numPDUSessionsMod
)
{
asn1cSequenceAdd
(
out
->
protocolIEs
.
list
,
E1AP_BearerContextModificationRequestIEs_t
,
modList
);
E1AP_NG_RAN_BearerContextModificationRequest_t
*
msgNGRAN
=
encode_ng_ran_to_mod
(
modList
);
msgNGRAN
->
id
=
E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Modify_List
;
msgNGRAN
->
criticality
=
E1AP_Criticality_reject
;
msgNGRAN
->
value
.
present
=
E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Modify_List
;
E1AP_PDU_Session_Resource_To_Modify_List_t
*
pdu2Setup
=
&
msgNGRAN
->
value
.
choice
.
PDU_Session_Resource_To_Modify_List
;
for
(
const
pdu_session_to_mod_t
*
i
=
in
->
pduSessionMod
;
i
<
in
->
pduSessionMod
+
in
->
numPDUSessionsMod
;
i
++
)
{
asn1cSequenceAdd
(
pdu2Setup
->
list
,
E1AP_PDU_Session_Resource_To_Modify_Item_t
,
ieC3_1
);
*
ieC3_1
=
e1_encode_pdu_session_to_mod_item
(
i
);
}
}
return
pdu
;
}
/** @brief Decode PDU Session Item to Setup (Bearer Context Modification Request) */
static
bool
e1_decode_pdu_session_to_setup_mod_item
(
pdu_session_to_setup_t
*
out
,
const
E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t
*
item
)
{
// PDU Session ID (M)
out
->
sessionId
=
item
->
pDU_Session_ID
;
// PDU Session Type (M)
out
->
sessionType
=
item
->
pDU_Session_Type
;
// S-NSSAI (M)
CHECK_E1AP_DEC
(
e1_decode_snssai
(
&
out
->
nssai
,
&
item
->
sNSSAI
));
// Security Indication (M)
CHECK_E1AP_DEC
(
e1_decode_security_indication
(
&
out
->
securityIndication
,
&
item
->
securityIndication
));
/* NG UL UP Transport Layer Information (M) */
_E1_EQ_CHECK_INT
(
item
->
nG_UL_UP_TNL_Information
.
present
,
E1AP_UP_TNL_Information_PR_gTPTunnel
);
CHECK_E1AP_DEC
(
e1_decode_up_tnl_info
(
&
out
->
UP_TL_information
,
&
item
->
nG_UL_UP_TNL_Information
));
/* DRB To Setup List ( > 1 item ) */
const
E1AP_DRB_To_Setup_Mod_List_NG_RAN_t
*
drb2SetupList
=
&
item
->
dRB_To_Setup_Mod_List_NG_RAN
;
_E1_EQ_CHECK_GENERIC
(
drb2SetupList
->
list
.
count
>
0
,
"%d"
,
drb2SetupList
->
list
.
count
,
0
);
out
->
numDRB2Setup
=
drb2SetupList
->
list
.
count
;
_E1_EQ_CHECK_INT
(
out
->
numDRB2Setup
,
1
);
// can only handle one DRB per PDU session
for
(
int
j
=
0
;
j
<
drb2SetupList
->
list
.
count
;
j
++
)
{
DRB_nGRAN_to_setup_t
*
drb
=
out
->
DRBnGRanList
+
j
;
const
E1AP_DRB_To_Setup_Mod_Item_NG_RAN_t
*
drb2Setup
=
drb2SetupList
->
list
.
array
[
j
];
// DRB ID (M)
drb
->
id
=
drb2Setup
->
dRB_ID
;
// SDAP Configuration (M)
CHECK_E1AP_DEC
(
e1_decode_sdap_config
(
&
drb
->
sdap_config
,
&
drb2Setup
->
sDAP_Configuration
));
// PDCP Configuration (M)
CHECK_E1AP_DEC
(
e1_decode_pdcp_config
(
&
drb
->
pdcp_config
,
&
drb2Setup
->
pDCP_Configuration
));
// Cell Group Information (M)
const
E1AP_Cell_Group_Information_t
*
cellGroupList
=
&
drb2Setup
->
cell_Group_Information
;
_E1_EQ_CHECK_GENERIC
(
cellGroupList
->
list
.
count
>
0
,
"%d"
,
cellGroupList
->
list
.
count
,
0
);
drb
->
numCellGroups
=
cellGroupList
->
list
.
count
;
for
(
int
k
=
0
;
k
<
cellGroupList
->
list
.
count
;
k
++
)
{
const
E1AP_Cell_Group_Information_Item_t
*
cg2Setup
=
cellGroupList
->
list
.
array
[
k
];
// Cell Group ID
drb
->
cellGroupList
[
k
]
=
cg2Setup
->
cell_Group_ID
;
}
// QoS Flows Information To Be Setup (M)
const
E1AP_QoS_Flow_QoS_Parameter_List_t
*
qos2SetupList
=
&
drb2Setup
->
flow_Mapping_Information
;
drb
->
numQosFlow2Setup
=
qos2SetupList
->
list
.
count
;
for
(
int
k
=
0
;
k
<
qos2SetupList
->
list
.
count
;
k
++
)
{
CHECK_E1AP_DEC
(
e1_decode_qos_flow_to_setup
(
drb
->
qosFlows
+
k
,
qos2SetupList
->
list
.
array
[
k
]));
}
}
return
true
;
}
/** @brief Decode PDU Session Item to modify (Bearer Context Modification Request) */
static
bool
e1_decode_pdu_session_to_mod_item
(
pdu_session_to_mod_t
*
out
,
const
E1AP_PDU_Session_Resource_To_Modify_Item_t
*
in
)
{
// PDU Session ID
out
->
sessionId
=
in
->
pDU_Session_ID
;
// DRB to modify list
E1AP_DRB_To_Modify_List_NG_RAN_t
*
drb2ModList
=
in
->
dRB_To_Modify_List_NG_RAN
;
out
->
numDRB2Modify
=
drb2ModList
->
list
.
count
;
for
(
int
j
=
0
;
j
<
drb2ModList
->
list
.
count
;
j
++
)
{
DRB_nGRAN_to_mod_t
*
drb
=
out
->
DRBnGRanModList
+
j
;
// DRB to modify item
E1AP_DRB_To_Modify_Item_NG_RAN_t
*
drb2Mod
=
drb2ModList
->
list
.
array
[
j
];
drb
->
id
=
drb2Mod
->
dRB_ID
;
// PDCP Config (O)
if
(
drb2Mod
->
pDCP_Configuration
)
{
drb
->
pdcp_config
=
malloc_or_fail
(
sizeof
(
*
drb
->
pdcp_config
));
CHECK_E1AP_DEC
(
e1_decode_pdcp_config
(
drb
->
pdcp_config
,
drb2Mod
->
pDCP_Configuration
));
}
// SDAP Config (O)
if
(
drb2Mod
->
sDAP_Configuration
)
{
drb
->
sdap_config
=
malloc_or_fail
(
sizeof
(
*
drb
->
sdap_config
));
CHECK_E1AP_DEC
(
e1_decode_sdap_config
(
drb
->
sdap_config
,
drb2Mod
->
sDAP_Configuration
));
}
// PDCP SN Status Request (O)
if
(
drb2Mod
->
pDCP_SN_Status_Request
)
{
drb
->
pdcp_sn_status_requested
=
true
;
}
// DL UP TNL parameters (O)
if
(
drb2Mod
->
dL_UP_Parameters
)
{
E1AP_UP_Parameters_t
*
dl_up_paramList
=
drb2Mod
->
dL_UP_Parameters
;
if
((
drb
->
numDlUpParam
=
decode_dl_up_parameters
(
drb
->
DlUpParamList
,
dl_up_paramList
))
<
0
)
{
PRINT_ERROR
(
"Failed to decode DL UP TNL parameters
\n
"
);
return
false
;
}
}
// QoS Flows Information To Be Setup (O)
E1AP_QoS_Flow_QoS_Parameter_List_t
*
qos2SetupList
=
drb2Mod
->
flow_Mapping_Information
;
drb
->
numQosFlow2Setup
=
qos2SetupList
->
list
.
count
;
for
(
int
k
=
0
;
k
<
qos2SetupList
->
list
.
count
;
k
++
)
{
CHECK_E1AP_DEC
(
e1_decode_qos_flow_to_setup
(
drb
->
qosFlows
+
k
,
qos2SetupList
->
list
.
array
[
k
]));
}
}
return
true
;
}
/**
* @brief E1 Bearer Modification Request message decoding
*/
bool
decode_E1_bearer_context_mod_request
(
const
E1AP_E1AP_PDU_t
*
pdu
,
e1ap_bearer_mod_req_t
*
out
)
{
DevAssert
(
pdu
!=
NULL
);
// Validate message type
_E1_EQ_CHECK_INT
(
pdu
->
present
,
E1AP_E1AP_PDU_PR_initiatingMessage
);
const
E1AP_InitiatingMessage_t
*
im
=
pdu
->
choice
.
initiatingMessage
;
_E1_EQ_CHECK_LONG
(
im
->
procedureCode
,
E1AP_ProcedureCode_id_bearerContextModification
);
_E1_EQ_CHECK_INT
(
im
->
value
.
present
,
E1AP_InitiatingMessage__value_PR_BearerContextModificationRequest
);
const
E1AP_BearerContextModificationRequest_t
*
in
=
&
im
->
value
.
choice
.
BearerContextModificationRequest
;
E1AP_BearerContextModificationRequestIEs_t
*
ie
=
NULL
;
// Mandatory IEs
// gNB-CU-CP UE E1AP ID (M)
E1AP_LIB_FIND_IE
(
E1AP_BearerContextModificationRequestIEs_t
,
ie
,
in
,
E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID
,
true
);
// gNB-CU-UP UE E1AP ID (M)
E1AP_LIB_FIND_IE
(
E1AP_BearerContextModificationRequestIEs_t
,
ie
,
in
,
E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID
,
true
);
// Loop through all IEs and extract
for
(
int
i
=
0
;
i
<
in
->
protocolIEs
.
list
.
count
;
i
++
)
{
ie
=
in
->
protocolIEs
.
list
.
array
[
i
];
switch
(
ie
->
id
)
{
case
E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID
:
_E1_EQ_CHECK_INT
(
ie
->
value
.
present
,
E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID
);
out
->
gNB_cu_cp_ue_id
=
ie
->
value
.
choice
.
GNB_CU_CP_UE_E1AP_ID
;
break
;
case
E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID
:
_E1_EQ_CHECK_INT
(
ie
->
value
.
present
,
E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_UP_UE_E1AP_ID
);
out
->
gNB_cu_up_ue_id
=
ie
->
value
.
choice
.
GNB_CU_UP_UE_E1AP_ID
;
break
;
case
E1AP_ProtocolIE_ID_id_SecurityInformation
:
out
->
secInfo
=
malloc_or_fail
(
sizeof
(
*
out
->
secInfo
));
CHECK_E1AP_DEC
(
e1_decode_security_info
(
out
->
secInfo
,
&
ie
->
value
.
choice
.
SecurityInformation
));
break
;
case
E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate
:
out
->
ueDlAggMaxBitRate
=
malloc_or_fail
(
sizeof
(
*
out
->
ueDlAggMaxBitRate
));
OCTET_STRING_TO_UINT32
(
&
ie
->
value
.
choice
.
BitRate
,
*
out
->
ueDlAggMaxBitRate
);
break
;
case
E1AP_ProtocolIE_ID_id_UEDLMaximumIntegrityProtectedDataRate
:
out
->
ueDlMaxIPBitRate
=
malloc_or_fail
(
sizeof
(
*
out
->
ueDlMaxIPBitRate
));
OCTET_STRING_TO_UINT32
(
&
ie
->
value
.
choice
.
BitRate
,
*
out
->
ueDlMaxIPBitRate
);
break
;
case
E1AP_ProtocolIE_ID_id_BearerContextStatusChange
:
_E1_EQ_CHECK_INT
(
ie
->
value
.
present
,
E1AP_BearerContextModificationRequestIEs__value_PR_BearerContextStatusChange
);
if
(
ie
->
value
.
choice
.
BearerContextStatusChange
==
E1AP_BearerContextStatusChange_suspend
)
{
out
->
bearerContextStatus
=
malloc_or_fail
(
sizeof
(
*
out
->
bearerContextStatus
));
*
out
->
bearerContextStatus
=
BEARER_SUSPEND
;
}
break
;
case
E1AP_ProtocolIE_ID_id_UE_Inactivity_Timer
:
out
->
inactivityTimer
=
malloc_or_fail
(
sizeof
(
*
out
->
inactivityTimer
));
*
out
->
inactivityTimer
=
ie
->
value
.
choice
.
Inactivity_Timer
;
break
;
case
E1AP_ProtocolIE_ID_id_DataDiscardRequired
:
out
->
dataDiscardRequired
=
ie
->
value
.
choice
.
DataDiscardRequired
;
break
;
case
E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest
:
{
_E1_EQ_CHECK_INT
(
ie
->
value
.
present
,
E1AP_BearerContextModificationRequestIEs__value_PR_System_BearerContextModificationRequest
);
E1AP_System_BearerContextModificationRequest_t
*
modReq
=
&
ie
->
value
.
choice
.
System_BearerContextModificationRequest
;
DevAssert
(
ie
->
value
.
choice
.
System_BearerContextModificationRequest
.
choice
.
nG_RAN_BearerContextModificationRequest
!=
NULL
);
// System IE can contain either UTRAN or NG-RAN
switch
(
modReq
->
present
)
{
case
E1AP_System_BearerContextModificationRequest_PR_NOTHING
:
PRINT_ERROR
(
"System Bearer Context Setup Request: no choice present
\n
"
);
break
;
case
E1AP_System_BearerContextModificationRequest_PR_e_UTRAN_BearerContextModificationRequest
:
PRINT_ERROR
(
"UTRAN in E1 Setup Modification Request not supported
\n
"
);
return
false
;
break
;
case
E1AP_System_BearerContextModificationRequest_PR_nG_RAN_BearerContextModificationRequest
:
// NG-RAN
AssertFatal
(
modReq
->
choice
.
nG_RAN_BearerContextModificationRequest
!=
NULL
,
"System_BearerContextSetupRequest->choice.nG_RAN_BearerContextSetupRequest shall not be null"
);
E1AP_ProtocolIE_Container_4932P26_t
*
msgNGRAN_list
=
(
E1AP_ProtocolIE_Container_4932P26_t
*
)
modReq
->
choice
.
nG_RAN_BearerContextModificationRequest
;
// loop through all items in the NG-RAN list (to setup, to modify, etc)
for
(
int
i
=
0
;
i
<
msgNGRAN_list
->
list
.
count
;
i
++
)
{
E1AP_NG_RAN_BearerContextModificationRequest_t
*
msgNGRAN
=
msgNGRAN_list
->
list
.
array
[
i
];
switch
(
msgNGRAN
->
id
)
{
case
E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_Mod_List
:
_E1_EQ_CHECK_LONG
(
msgNGRAN
->
id
,
E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_Mod_List
);
_E1_EQ_CHECK_INT
(
msgNGRAN
->
value
.
present
,
E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Setup_Mod_List
);
E1AP_PDU_Session_Resource_To_Setup_Mod_List_t
*
setupList
=
&
msgNGRAN
->
value
.
choice
.
PDU_Session_Resource_To_Setup_Mod_List
;
// PDU Session Resource To Modify List
out
->
numPDUSessions
=
setupList
->
list
.
count
;
// Loop through all PDU sessions
for
(
int
i
=
0
;
i
<
setupList
->
list
.
count
;
i
++
)
{
pdu_session_to_setup_t
*
pdu_session
=
out
->
pduSession
+
i
;
E1AP_PDU_Session_Resource_To_Setup_Mod_Item_t
*
pdu2Setup
=
setupList
->
list
.
array
[
i
];
CHECK_E1AP_DEC
(
e1_decode_pdu_session_to_setup_mod_item
(
pdu_session
,
pdu2Setup
));
}
break
;
case
E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Modify_List
:
/* PDU Session Resource To Modify List (see 9.3.3.11 of TS 38.463) */
_E1_EQ_CHECK_INT
(
msgNGRAN
->
value
.
present
,
E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Modify_List
);
E1AP_PDU_Session_Resource_To_Modify_List_t
*
modList
=
&
msgNGRAN
->
value
.
choice
.
PDU_Session_Resource_To_Modify_List
;
out
->
numPDUSessionsMod
=
modList
->
list
.
count
;
// Loop through all PDU sessions
for
(
int
i
=
0
;
i
<
modList
->
list
.
count
;
i
++
)
{
CHECK_E1AP_DEC
(
e1_decode_pdu_session_to_mod_item
(
out
->
pduSessionMod
+
i
,
modList
->
list
.
array
[
i
]));
}
break
;
default:
PRINT_ERROR
(
"Unknown msgNGRAN->id in E1 Setup Modification Request
\n
"
);
return
false
;
break
;
}
}
break
;
default:
PRINT_ERROR
(
"System Bearer Context Modification Request: No valid choice present.
\n
"
);
break
;
}
break
;
}
default:
PRINT_ERROR
(
"System Bearer Context Modification Request: this IE is not implemented (or) invalid IE detected
\n
"
);
return
false
;
break
;
}
}
return
true
;
}
/** @brief Deep copy for DRB Item to modify */
static
DRB_nGRAN_to_mod_t
cp_drb_to_mod_item
(
const
DRB_nGRAN_to_mod_t
*
msg
)
{
DRB_nGRAN_to_mod_t
cp
=
{
0
};
// shallow copy
cp
=
*
msg
;
_E1_CP_OPTIONAL_IE
(
&
cp
,
msg
,
sdap_config
);
_E1_CP_OPTIONAL_IE
(
&
cp
,
msg
,
pdcp_config
);
return
cp
;
}
/** @brief Deep copy for PDU Session Item to modify */
static
pdu_session_to_mod_t
cp_pdu_session_to_mod_item
(
const
pdu_session_to_mod_t
*
msg
)
{
pdu_session_to_mod_t
cp
=
{
0
};
// shallow copy
cp
=
*
msg
;
// DRB to setup list
for
(
int
j
=
0
;
j
<
msg
->
numDRB2Setup
;
j
++
)
cp
.
DRBnGRanList
[
j
]
=
cp_drb_to_setup_item
(
&
msg
->
DRBnGRanList
[
j
]);
// DRB to modify list
for
(
int
j
=
0
;
j
<
msg
->
numDRB2Modify
;
j
++
)
cp
.
DRBnGRanModList
[
j
]
=
cp_drb_to_mod_item
(
&
msg
->
DRBnGRanModList
[
j
]);
_E1_CP_OPTIONAL_IE
(
&
cp
,
msg
,
securityIndication
);
_E1_CP_OPTIONAL_IE
(
&
cp
,
msg
,
UP_TL_information
);
return
cp
;
}
/** @brief Deep copy for Bearer Context Modification Request */
e1ap_bearer_mod_req_t
cp_bearer_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
msg
)
{
e1ap_bearer_mod_req_t
cp
=
{
0
};
// Shallow copy
cp
=
*
msg
;
// Deep copy for optional fields
_E1_CP_OPTIONAL_IE
(
&
cp
,
msg
,
secInfo
);
_E1_CP_OPTIONAL_IE
(
&
cp
,
msg
,
bearerContextStatus
);
_E1_CP_OPTIONAL_IE
(
&
cp
,
msg
,
inactivityTimer
);
// Deep copy PDU sessions
for
(
int
i
=
0
;
i
<
msg
->
numPDUSessions
;
i
++
)
cp
.
pduSession
[
i
]
=
cp_pdu_session_item
(
&
msg
->
pduSession
[
i
]);
for
(
int
i
=
0
;
i
<
msg
->
numPDUSessionsMod
;
i
++
)
cp
.
pduSessionMod
[
i
]
=
cp_pdu_session_to_mod_item
(
&
msg
->
pduSessionMod
[
i
]);
return
cp
;
}
/** @brief Equality check for Security Indication IE (3GPP TS 38.463 9.3.1.23) */
static
bool
eq_security_ind
(
security_indication_t
*
a
,
security_indication_t
*
b
)
{
_E1_EQ_CHECK_INT
(
a
->
confidentialityProtectionIndication
,
b
->
confidentialityProtectionIndication
);
_E1_EQ_CHECK_INT
(
a
->
integrityProtectionIndication
,
b
->
integrityProtectionIndication
);
_E1_EQ_CHECK_LONG
(
a
->
maxIPrate
,
b
->
maxIPrate
);
return
true
;
}
/**
* @brief Equality check for DRB_nGRAN_to_mod_t
*/
static
bool
eq_drb_to_mod
(
const
DRB_nGRAN_to_mod_t
*
a
,
const
DRB_nGRAN_to_mod_t
*
b
)
{
bool
result
=
true
;
_E1_EQ_CHECK_LONG
(
a
->
id
,
b
->
id
);
_E1_EQ_CHECK_INT
(
a
->
numQosFlow2Setup
,
b
->
numQosFlow2Setup
);
for
(
int
i
=
0
;
i
<
a
->
numQosFlow2Setup
;
i
++
)
{
result
&=
eq_qos_flow
(
&
a
->
qosFlows
[
i
],
&
b
->
qosFlows
[
i
]);
}
_E1_EQ_CHECK_INT
(
a
->
numDlUpParam
,
b
->
numDlUpParam
);
for
(
int
i
=
0
;
i
<
a
->
numDlUpParam
;
i
++
)
{
_E1_EQ_CHECK_INT
(
a
->
DlUpParamList
[
i
].
cell_group_id
,
b
->
DlUpParamList
[
i
].
cell_group_id
);
eq_up_tl_info
(
&
a
->
DlUpParamList
[
i
].
tl_info
,
&
b
->
DlUpParamList
[
i
].
tl_info
);
}
if
(
a
->
pdcp_config
&&
b
->
pdcp_config
)
result
&=
eq_pdcp_config
(
a
->
pdcp_config
,
b
->
pdcp_config
);
if
(
a
->
sdap_config
&&
b
->
sdap_config
)
result
&=
eq_sdap_config
(
a
->
sdap_config
,
b
->
sdap_config
);
return
result
;
}
/** @brief Equality check for PDU session item to modify */
static
bool
eq_pdu_session_to_mod_item
(
const
pdu_session_to_mod_t
*
a
,
const
pdu_session_to_mod_t
*
b
)
{
_E1_EQ_CHECK_LONG
(
a
->
sessionId
,
b
->
sessionId
);
if
(
a
->
UP_TL_information
&&
a
->
UP_TL_information
)
{
if
(
!
eq_up_tl_info
(
a
->
UP_TL_information
,
b
->
UP_TL_information
))
return
false
;
}
_E1_EQ_CHECK_INT
(
a
->
numDRB2Setup
,
b
->
numDRB2Setup
);
for
(
int
i
=
0
;
i
<
a
->
numDRB2Setup
;
i
++
)
{
if
(
!
eq_drb_to_setup
(
&
a
->
DRBnGRanList
[
i
],
&
b
->
DRBnGRanList
[
i
]))
return
false
;
}
_E1_EQ_CHECK_LONG
(
a
->
numDRB2Modify
,
b
->
numDRB2Modify
);
for
(
int
i
=
0
;
i
<
a
->
numDRB2Modify
;
i
++
)
{
if
(
!
eq_drb_to_mod
(
&
a
->
DRBnGRanModList
[
i
],
&
b
->
DRBnGRanModList
[
i
]))
return
false
;
}
if
(
a
->
securityIndication
&&
b
->
securityIndication
)
{
eq_security_ind
(
a
->
securityIndication
,
b
->
securityIndication
);
}
return
true
;
}
/**
* @brief E1AP Bearer Modification Request equality check
*/
bool
eq_bearer_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
a
,
const
e1ap_bearer_mod_req_t
*
b
)
{
// basic members
_E1_EQ_CHECK_INT
(
a
->
gNB_cu_cp_ue_id
,
b
->
gNB_cu_cp_ue_id
);
_E1_EQ_CHECK_INT
(
a
->
gNB_cu_up_ue_id
,
b
->
gNB_cu_up_ue_id
);
_E1_EQ_CHECK_INT
(
a
->
numPDUSessions
,
b
->
numPDUSessions
);
_E1_EQ_CHECK_INT
(
a
->
numPDUSessionsMod
,
b
->
numPDUSessionsMod
);
// Security Info
_E1_EQ_CHECK_OPTIONAL_PTR
(
a
,
b
,
secInfo
);
if
(
a
->
secInfo
&&
b
->
secInfo
)
{
_E1_EQ_CHECK_LONG
(
a
->
secInfo
->
cipheringAlgorithm
,
b
->
secInfo
->
cipheringAlgorithm
);
_E1_EQ_CHECK_LONG
(
a
->
secInfo
->
integrityProtectionAlgorithm
,
b
->
secInfo
->
integrityProtectionAlgorithm
);
_E1_EQ_CHECK_STR
(
a
->
secInfo
->
encryptionKey
,
b
->
secInfo
->
encryptionKey
);
_E1_EQ_CHECK_STR
(
a
->
secInfo
->
integrityProtectionKey
,
b
->
secInfo
->
integrityProtectionKey
);
}
// Bearer Context Status (O)
_E1_EQ_CHECK_OPTIONAL_IE
(
a
,
b
,
bearerContextStatus
,
_E1_EQ_CHECK_INT
);
// Inactivity Timer (O)
_E1_EQ_CHECK_OPTIONAL_PTR
(
a
,
b
,
inactivityTimer
);
if
(
a
->
inactivityTimer
&&
b
->
inactivityTimer
)
_E1_EQ_CHECK_INT
(
*
a
->
inactivityTimer
,
*
b
->
inactivityTimer
);
// Activity Notification Level (O)
// PDU Sessions
if
(
a
->
numPDUSessions
!=
b
->
numPDUSessions
)
return
false
;
for
(
int
i
=
0
;
i
<
a
->
numPDUSessions
;
i
++
)
{
if
(
!
eq_pdu_session_item
(
&
a
->
pduSession
[
i
],
&
b
->
pduSession
[
i
]))
return
false
;
}
// Modified PDU Sessions
if
(
a
->
numPDUSessionsMod
!=
b
->
numPDUSessionsMod
)
return
false
;
for
(
int
i
=
0
;
i
<
a
->
numPDUSessionsMod
;
i
++
)
{
if
(
!
eq_pdu_session_to_mod_item
(
&
a
->
pduSessionMod
[
i
],
&
b
->
pduSessionMod
[
i
]))
return
false
;
}
return
true
;
}
/* Free DRB to modify item */
static
void
free_drb_to_mod_item
(
const
DRB_nGRAN_to_mod_t
*
msg
)
{
free
(
msg
->
pdcp_config
);
free
(
msg
->
sdap_config
);
}
/* Free PDU Session to modify item */
void
free_pdu_session_to_mod_item
(
const
pdu_session_to_mod_t
*
msg
)
{
free
(
msg
->
securityIndication
);
free
(
msg
->
UP_TL_information
);
for
(
int
i
=
0
;
i
<
msg
->
numDRB2Modify
;
i
++
)
free_drb_to_mod_item
(
&
msg
->
DRBnGRanModList
[
i
]);
for
(
int
i
=
0
;
i
<
msg
->
numDRB2Setup
;
i
++
)
free_drb_to_setup_item
(
&
msg
->
DRBnGRanList
[
i
]);
}
/**
* @brief E1AP Bearer Context Modification Request memory management
*/
void
free_e1ap_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
msg
)
{
free
(
msg
->
secInfo
);
free
(
msg
->
ueDlAggMaxBitRate
);
free
(
msg
->
ueDlMaxIPBitRate
);
free
(
msg
->
bearerContextStatus
);
free
(
msg
->
inactivityTimer
);
for
(
int
i
=
0
;
i
<
msg
->
numPDUSessions
;
i
++
)
free_pdu_session_to_setup_item
(
&
msg
->
pduSession
[
i
]);
for
(
int
i
=
0
;
i
<
msg
->
numPDUSessionsMod
;
i
++
)
free_pdu_session_to_mod_item
(
&
msg
->
pduSessionMod
[
i
]);
}
openair2/E1AP/lib/e1ap_bearer_context_management.h
View file @
b061b576
...
...
@@ -40,4 +40,10 @@ void free_e1ap_context_setup_response(const e1ap_bearer_setup_resp_t *msg);
e1ap_bearer_setup_resp_t
cp_bearer_context_setup_response
(
const
e1ap_bearer_setup_resp_t
*
msg
);
bool
eq_bearer_context_setup_response
(
const
e1ap_bearer_setup_resp_t
*
a
,
const
e1ap_bearer_setup_resp_t
*
b
);
struct
E1AP_E1AP_PDU
*
encode_E1_bearer_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
msg
);
bool
decode_E1_bearer_context_mod_request
(
const
struct
E1AP_E1AP_PDU
*
pdu
,
e1ap_bearer_mod_req_t
*
out
);
void
free_e1ap_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
msg
);
e1ap_bearer_mod_req_t
cp_bearer_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
msg
);
bool
eq_bearer_context_mod_request
(
const
e1ap_bearer_mod_req_t
*
a
,
const
e1ap_bearer_mod_req_t
*
b
);
#endif
/* E1AP_BEARER_CONTEXT_MANAGEMENT_H_ */
openair2/E1AP/lib/e1ap_lib_includes.h
View file @
b061b576
...
...
@@ -67,5 +67,11 @@
#include "E1AP_Transport-UP-Layer-Addresses-Info-To-Remove-Item.h"
#include "E1AP_GTPTLAs.h"
#include "E1AP_GTPTLA-Item.h"
// E1 Bearer Context Modification Request
#include "E1AP_PDU-Session-Resource-To-Modify-Item.h"
#include "E1AP_DRB-To-Modify-List-NG-RAN.h"
#include "E1AP_DRB-To-Modify-Item-NG-RAN.h"
#include "E1AP_PDU-Session-Resource-To-Setup-Mod-Item.h"
#include "E1AP_DRB-To-Setup-Mod-Item-NG-RAN.h"
#endif
/* E1AP_LIB_INCLUDES_H_ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment