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
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
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-RAN
Commits
679b6be9
Commit
679b6be9
authored
Sep 17, 2020
by
Xue Song
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Modify RRCSetupRequest message
Add RRCSetupComplete message
parent
b0c79092
Changes
10
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
504 additions
and
29 deletions
+504
-29
cmake_targets/CMakeLists.txt
cmake_targets/CMakeLists.txt
+1
-0
openair2/COMMON/mac_messages_def.h
openair2/COMMON/mac_messages_def.h
+1
-1
openair2/COMMON/mac_messages_types.h
openair2/COMMON/mac_messages_types.h
+2
-2
openair2/COMMON/pdcp_messages_def.h
openair2/COMMON/pdcp_messages_def.h
+3
-0
openair2/COMMON/pdcp_messages_types.h
openair2/COMMON/pdcp_messages_types.h
+13
-0
openair2/RRC/NR/nr_rrc_defs.h
openair2/RRC/NR/nr_rrc_defs.h
+15
-3
openair2/RRC/NR/rrc_gNB.c
openair2/RRC/NR/rrc_gNB.c
+230
-22
openair2/RRC/NR/rrc_gNB_NGAP.c
openair2/RRC/NR/rrc_gNB_NGAP.c
+187
-0
openair2/RRC/NR/rrc_gNB_NGAP.h
openair2/RRC/NR/rrc_gNB_NGAP.h
+51
-0
openair2/RRC/NR/rrc_gNB_UE_context.c
openair2/RRC/NR/rrc_gNB_UE_context.c
+1
-1
No files found.
cmake_targets/CMakeLists.txt
View file @
679b6be9
...
...
@@ -1835,6 +1835,7 @@ set(L2_NR_SRC
${
NR_RRC_DIR
}
/rrc_gNB_internode.c
${
NR_RRC_DIR
}
/rrc_gNB_reconfig.c
${
NR_RRC_DIR
}
/rrc_gNB_UE_context.c
${
NR_RRC_DIR
}
/rrc_gNB_NGAP.c
)
set
(
L2_SRC_UE
...
...
openair2/COMMON/mac_messages_def.h
View file @
679b6be9
...
...
@@ -42,7 +42,7 @@ MESSAGE_DEF(RRC_MAC_CCCH_DATA_CNF, MESSAGE_PRIORITY_MED_PLUS, RrcMacCcchDat
MESSAGE_DEF
(
RRC_MAC_CCCH_DATA_IND
,
MESSAGE_PRIORITY_MED_PLUS
,
RrcMacCcchDataInd
,
rrc_mac_ccch_data_ind
)
// gNB
MESSAGE_DEF
(
NR_RRC_MAC_CCCH_DATA_IND
,
MESSAGE_PRIORITY_MED_PLUS
,
NR
_
RrcMacCcchDataInd
,
nr_rrc_mac_ccch_data_ind
)
MESSAGE_DEF
(
NR_RRC_MAC_CCCH_DATA_IND
,
MESSAGE_PRIORITY_MED_PLUS
,
NRRrcMacCcchDataInd
,
nr_rrc_mac_ccch_data_ind
)
MESSAGE_DEF
(
RRC_MAC_MCCH_DATA_REQ
,
MESSAGE_PRIORITY_MED_PLUS
,
RrcMacMcchDataReq
,
rrc_mac_mcch_data_req
)
MESSAGE_DEF
(
RRC_MAC_MCCH_DATA_IND
,
MESSAGE_PRIORITY_MED_PLUS
,
RrcMacMcchDataInd
,
rrc_mac_mcch_data_ind
)
...
...
openair2/COMMON/mac_messages_types.h
View file @
679b6be9
...
...
@@ -127,7 +127,7 @@ typedef struct RrcMacCcchDataInd_s {
int
CC_id
;
}
RrcMacCcchDataInd
;
typedef
struct
NR
_
RrcMacCcchDataInd_s
{
typedef
struct
NRRrcMacCcchDataInd_s
{
uint32_t
frame
;
uint8_t
sub_frame
;
uint16_t
rnti
;
...
...
@@ -135,7 +135,7 @@ typedef struct NR_RrcMacCcchDataInd_s {
uint8_t
sdu
[
CCCH_SDU_SIZE
];
uint8_t
gnb_index
;
int
CC_id
;
}
NR
_
RrcMacCcchDataInd
;
}
NRRrcMacCcchDataInd
;
typedef
struct
RrcMacMcchDataReq_s
{
uint32_t
frame
;
...
...
openair2/COMMON/pdcp_messages_def.h
View file @
679b6be9
...
...
@@ -31,3 +31,6 @@
MESSAGE_DEF
(
RRC_DCCH_DATA_REQ
,
MESSAGE_PRIORITY_MED_PLUS
,
RrcDcchDataReq
,
rrc_dcch_data_req
)
MESSAGE_DEF
(
RRC_DCCH_DATA_IND
,
MESSAGE_PRIORITY_MED_PLUS
,
RrcDcchDataInd
,
rrc_dcch_data_ind
)
MESSAGE_DEF
(
RRC_PCCH_DATA_REQ
,
MESSAGE_PRIORITY_MED_PLUS
,
RrcPcchDataReq
,
rrc_pcch_data_req
)
// gNB
MESSAGE_DEF
(
NR_RRC_DCCH_DATA_IND
,
MESSAGE_PRIORITY_MED_PLUS
,
NRRrcDcchDataInd
,
nr_rrc_dcch_data_ind
)
openair2/COMMON/pdcp_messages_types.h
View file @
679b6be9
...
...
@@ -35,6 +35,9 @@
#define RRC_DCCH_DATA_IND(mSGpTR) (mSGpTR)->ittiMsg.rrc_dcch_data_ind
#define RRC_PCCH_DATA_REQ(mSGpTR) (mSGpTR)->ittiMsg.rrc_pcch_data_req
// gNB
#define NR_RRC_DCCH_DATA_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_dcch_data_ind
//-------------------------------------------------------------------------------------------//
// Messages between RRC and PDCP layers
typedef
struct
RrcDcchDataReq_s
{
...
...
@@ -61,6 +64,16 @@ typedef struct RrcDcchDataInd_s {
uint8_t
eNB_index
;
// LG: needed in UE
}
RrcDcchDataInd
;
typedef
struct
NRRrcDcchDataInd_s
{
uint32_t
frame
;
uint8_t
dcch_index
;
uint32_t
sdu_size
;
uint8_t
*
sdu_p
;
uint16_t
rnti
;
uint8_t
module_id
;
uint8_t
gNB_index
;
// LG: needed in UE
}
NRRrcDcchDataInd
;
typedef
struct
RrcPcchDataReq_s
{
uint32_t
sdu_size
;
uint8_t
*
sdu_p
;
...
...
openair2/RRC/NR/nr_rrc_defs.h
View file @
679b6be9
...
...
@@ -169,8 +169,9 @@ typedef struct UE_RRC_INFO_NR_s {
typedef
struct
UE_S_TMSI_NR_s
{
boolean_t
presence
;
mme_code_t
mme_code
;
m_tmsi_t
m_tmsi
;
uint16_t
amf_set_id
;
uint8_t
amf_pointer
;
uint32_t
fiveg_tmsi
;
}
__attribute__
((
__packed__
))
NR_UE_S_TMSI
;
...
...
@@ -247,6 +248,14 @@ typedef struct SRB_INFO_TABLE_ENTRY_NR_s {
uint32_t
Next_check_frame
;
}
NR_SRB_INFO_TABLE_ENTRY
;
typedef
struct
nr_rrc_guami_s
{
uint16_t
mcc
;
uint16_t
mnc
;
uint8_t
mnc_len
;
uint8_t
amf_region_id
;
uint16_t
amf_set_id
;
uint8_t
amf_pointer
;
}
nr_rrc_guami_t
;
typedef
struct
gNB_RRC_UE_s
{
uint8_t
primaryCC_id
;
...
...
@@ -292,7 +301,9 @@ typedef struct gNB_RRC_UE_s {
uint64_t
random_ue_identity
;
/* Information from UE RRC Setup Request */
uint64_t
Initialue_identity_5g_s_TMSI
;
NR_UE_S_TMSI
Initialue_identity_5g_s_TMSI
;
uint64_t
ng_5G_S_TMSI_Part1
;
uint16_t
ng_5G_S_TMSI_Part2
;
NR_EstablishmentCause_t
establishment_cause
;
/* Information from UE RRC ConnectionReestablishmentRequest */
...
...
@@ -303,6 +314,7 @@ typedef struct gNB_RRC_UE_s {
/* Information from S1AP initial_context_setup_req */
uint32_t
gNB_ue_s1ap_id
:
24
;
nr_rrc_guami_t
ue_guami
;
security_capabilities_t
security_capabilities
;
...
...
openair2/RRC/NR/rrc_gNB.c
View file @
679b6be9
This diff is collapsed.
Click to expand it.
openair2/RRC/NR/rrc_gNB_NGAP.c
0 → 100644
View file @
679b6be9
/*
* 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 "rrc_gNB_NGAP.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
#include "rrc_eNB_S1AP.h"
#include "gnb_config.h"
#include "common/ran_context.h"
#include "gtpv1u.h"
#include "asn1_conversions.h"
#include "intertask_interface.h"
#include "pdcp.h"
#include "pdcp_primitives.h"
#include "msc.h"
#include "gtpv1u_eNB_task.h"
#include "RRC/LTE/rrc_eNB_GTPV1U.h"
#include "S1AP_NAS-PDU.h"
#include "executables/softmodem-common.h"
extern
RAN_CONTEXT_t
RC
;
/* Value to indicate an invalid UE initial id */
static
const
uint16_t
UE_INITIAL_ID_INVALID
=
0
;
/*! \fn uint16_t get_next_ue_initial_id(uint8_t mod_id)
*\brief provide an UE initial ID for NGAP initial communication.
*\param mod_id Instance ID of gNB.
*\return the UE initial ID.
*/
//------------------------------------------------------------------------------
static
uint16_t
get_next_ue_initial_id
(
const
module_id_t
mod_id
)
//------------------------------------------------------------------------------
{
static
uint16_t
ue_initial_id
[
NUMBER_OF_gNB_MAX
];
ue_initial_id
[
mod_id
]
++
;
/* Never use UE_INITIAL_ID_INVALID this is the invalid id! */
if
(
ue_initial_id
[
mod_id
]
==
UE_INITIAL_ID_INVALID
)
{
ue_initial_id
[
mod_id
]
++
;
}
return
ue_initial_id
[
mod_id
];
}
//------------------------------------------------------------------------------
/*
* Initial UE NAS message on S1AP.
*/
void
rrc_gNB_send_NGAP_NAS_FIRST_REQ
(
const
protocol_ctxt_t
*
const
ctxt_pP
,
rrc_gNB_ue_context_t
*
ue_context_pP
,
NR_RRCSetupComplete_IEs_t
*
rrcSetupComplete
)
//------------------------------------------------------------------------------
{
gNB_RRC_INST
*
rrc
=
RC
.
nrrrc
[
ctxt_pP
->
module_id
];
MessageDef
*
message_p
=
NULL
;
rrc_ue_ngap_ids_t
*
rrc_ue_ngap_ids_p
=
NULL
;
hashtable_rc_t
h_rc
;
message_p
=
itti_alloc_new_message
(
TASK_RRC_GNB
,
NGAP_NAS_FIRST_REQ
);
memset
(
&
message_p
->
ittiMsg
.
ngap_nas_first_req
,
0
,
sizeof
(
ngap_nas_first_req_t
));
ue_context_pP
->
ue_context
.
ue_initial_id
=
get_next_ue_initial_id
(
ctxt_pP
->
module_id
);
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_initial_id
=
ue_context_pP
->
ue_context
.
ue_initial_id
;
rrc_ue_ngap_ids_p
=
malloc
(
sizeof
(
rrc_ue_ngap_ids_t
));
rrc_ue_ngap_ids_p
->
ue_initial_id
=
ue_context_pP
->
ue_context
.
ue_initial_id
;
rrc_ue_ngap_ids_p
->
gNB_ue_ngap_id
=
UE_INITIAL_ID_INVALID
;
rrc_ue_ngap_ids_p
->
ue_rnti
=
ctxt_pP
->
rnti
;
// h_rc = hashtable_insert(RC.nrrrc[ctxt_pP->module_id]->initial_id2_s1ap_ids,
// (hash_key_t)ue_context_pP->ue_context.ue_initial_id,
// rrc_ue_s1ap_ids_p);
// if (h_rc != HASH_TABLE_OK) {
// LOG_E(S1AP, "[eNB %d] Error while hashtable_insert in initial_id2_s1ap_ids ue_initial_id %u\n",
// ctxt_pP->module_id,
// ue_context_pP->ue_context.ue_initial_id);
// }
/* Assume that cause is coded in the same way in RRC and NGap, just check that the value is in NGap range */
AssertFatal
(
ue_context_pP
->
ue_context
.
establishment_cause
<
NGAP_RRC_CAUSE_LAST
,
"Establishment cause invalid (%jd/%d) for gNB %d!"
,
ue_context_pP
->
ue_context
.
establishment_cause
,
NGAP_RRC_CAUSE_LAST
,
ctxt_pP
->
module_id
);
NGAP_NAS_FIRST_REQ
(
message_p
).
establishment_cause
=
ue_context_pP
->
ue_context
.
establishment_cause
;
/* Forward NAS message */
NGAP_NAS_FIRST_REQ
(
message_p
).
nas_pdu
.
buffer
=
rrcSetupComplete
->
dedicatedNAS_Message
.
buf
;
NGAP_NAS_FIRST_REQ
(
message_p
).
nas_pdu
.
length
=
rrcSetupComplete
->
dedicatedNAS_Message
.
size
;
// extract_imsi(NGAP_NAS_FIRST_REQ (message_p).nas_pdu.buffer,
// NGAP_NAS_FIRST_REQ (message_p).nas_pdu.length,
// ue_context_pP);
/* Fill UE identities with available information */
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
presenceMask
=
NGAP_UE_IDENTITIES_NONE
;
/* Fill s-TMSI */
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
s_tmsi
.
amf_set_id
=
ue_context_pP
->
ue_context
.
Initialue_identity_5g_s_TMSI
.
amf_set_id
;
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
s_tmsi
.
amf_pointer
=
ue_context_pP
->
ue_context
.
Initialue_identity_5g_s_TMSI
.
amf_pointer
;
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
s_tmsi
.
m_tmsi
=
ue_context_pP
->
ue_context
.
Initialue_identity_5g_s_TMSI
.
fiveg_tmsi
;
/* selected_plmn_identity: IE is 1-based, convert to 0-based (C array) */
int
selected_plmn_identity
=
rrcSetupComplete
->
selectedPLMN_Identity
-
1
;
NGAP_NAS_FIRST_REQ
(
message_p
).
selected_plmn_identity
=
selected_plmn_identity
;
if
(
rrcSetupComplete
->
registeredAMF
!=
NULL
)
{
NR_RegisteredAMF_t
*
r_amf
=
rrcSetupComplete
->
registeredAMF
;
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
presenceMask
|=
NGAP_UE_IDENTITIES_guami
;
if
(
r_amf
->
plmn_Identity
!=
NULL
)
{
if
((
r_amf
->
plmn_Identity
->
mcc
!=
NULL
)
&&
(
r_amf
->
plmn_Identity
->
mcc
->
list
.
count
>
0
))
{
/* Use first indicated PLMN MCC if it is defined */
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
mcc
=
*
r_amf
->
plmn_Identity
->
mcc
->
list
.
array
[
selected_plmn_identity
];
LOG_I
(
NGAP
,
"[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MCC %u ue %x
\n
"
,
ctxt_pP
->
module_id
,
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
mcc
,
ue_context_pP
->
ue_context
.
rnti
);
}
if
(
r_amf
->
plmn_Identity
->
mnc
.
list
.
count
>
0
)
{
/* Use first indicated PLMN MNC if it is defined */
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
mnc
=
*
r_amf
->
plmn_Identity
->
mnc
.
list
.
array
[
selected_plmn_identity
];
LOG_I
(
NGAP
,
"[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUMMEI MNC %u ue %x
\n
"
,
ctxt_pP
->
module_id
,
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
mnc
,
ue_context_pP
->
ue_context
.
rnti
);
}
}
else
{
/* TODO */
}
/* amf_Identifier */
uint32_t
amf_Id
=
BIT_STRING_to_uint32
(
&
r_amf
->
amf_Identifier
);
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_region_id
=
amf_Id
>>
16
;
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_set_id
=
ue_context_pP
->
ue_context
.
Initialue_identity_5g_s_TMSI
.
amf_set_id
;
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_pointer
=
ue_context_pP
->
ue_context
.
Initialue_identity_5g_s_TMSI
.
amf_pointer
;
ue_context_pP
->
ue_context
.
ue_guami
.
mcc
=
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
mcc
;
ue_context_pP
->
ue_context
.
ue_guami
.
mnc
=
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
mnc
;
ue_context_pP
->
ue_context
.
ue_guami
.
mnc_len
=
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
mnc_len
;
ue_context_pP
->
ue_context
.
ue_guami
.
amf_region_id
=
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_region_id
;
ue_context_pP
->
ue_context
.
ue_guami
.
amf_set_id
=
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_set_id
;
ue_context_pP
->
ue_context
.
ue_guami
.
amf_pointer
=
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_pointer
;
MSC_LOG_TX_MESSAGE
(
MSC_NGAP_GNB
,
MSC_NGAP_AMF
,
(
const
char
*
)
&
message_p
->
ittiMsg
.
ngap_nas_first_req
,
sizeof
(
ngap_nas_first_req_t
),
MSC_AS_TIME_FMT
" NGAP_NAS_FIRST_REQ gNB %u UE %x"
,
MSC_AS_TIME_ARGS
(
ctxt_pP
),
ctxt_pP
->
module_id
,
ctxt_pP
->
rnti
);
LOG_I
(
NGAP
,
"[gNB %d] Build NGAP_NAS_FIRST_REQ adding in s_TMSI: GUAMI amf_set_id %u amf_region_id %u ue %x
\n
"
,
ctxt_pP
->
module_id
,
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_set_id
,
NGAP_NAS_FIRST_REQ
(
message_p
).
ue_identity
.
guami
.
amf_region_id
,
ue_context_pP
->
ue_context
.
rnti
);
}
itti_send_msg_to_task
(
TASK_NGAP
,
ctxt_pP
->
instance
,
message_p
);
}
\ No newline at end of file
openair2/RRC/NR/rrc_gNB_NGAP.h
0 → 100644
View file @
679b6be9
/*
* 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 RRC_GNB_NGAP_H_
#define RRC_GNB_NGAP_H_
#include "rrc_gNB_UE_context.h"
#include "nr_rrc_defs.h"
#include "nr_rrc_extern.h"
#include "NR_RRCSetupComplete-IEs.h"
#include "NR_RegisteredAMF.h"
typedef
struct
rrc_ue_ngap_ids_s
{
/* Tree related data */
RB_ENTRY
(
rrc_ue_ngap_ids_s
)
entries
;
// keys
uint16_t
ue_initial_id
;
uint32_t
gNB_ue_ngap_id
;
// value
rnti_t
ue_rnti
;
}
rrc_ue_ngap_ids_t
;
void
rrc_gNB_send_NGAP_NAS_FIRST_REQ
(
const
protocol_ctxt_t
*
const
ctxt_pP
,
rrc_gNB_ue_context_t
*
ue_context_pP
,
NR_RRCSetupComplete_IEs_t
*
rrcSetupComplete
);
#endif
openair2/RRC/NR/rrc_gNB_UE_context.c
View file @
679b6be9
...
...
@@ -251,7 +251,7 @@ rrc_gNB_ue_context_5g_s_tmsi_exist(
LOG_I
(
NR_RRC
,
"checking for UE 5G S-TMSI %x: rnti %x
\n
"
,
s_TMSI
,
ue_context_p
->
ue_context
.
rnti
);
if
(
ue_context_p
->
ue_context
.
Initialue_identity_5g_s_TMSI
==
s_TMSI
)
{
if
(
ue_context_p
->
ue_context
.
ng_5G_S_TMSI_Part1
==
s_TMSI
)
{
return
ue_context_p
;
}
}
...
...
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