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
a2f41597
Commit
a2f41597
authored
Jun 04, 2022
by
Sakthivel Velumani
1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixed bugs in E1 setup procedure
E1 setup request decoding successful
parent
6106207b
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
87 additions
and
29 deletions
+87
-29
ci-scripts/conf_files/gNB_SA_CU.conf
ci-scripts/conf_files/gNB_SA_CU.conf
+4
-4
common/utils/LOG/log.c
common/utils/LOG/log.c
+1
-0
openair2/E1AP/e1ap.c
openair2/E1AP/e1ap.c
+48
-9
openair2/E1AP/e1ap_common.c
openair2/E1AP/e1ap_common.c
+22
-11
openair2/E1AP/e1ap_common.h
openair2/E1AP/e1ap_common.h
+2
-2
openair2/GNB_APP/gnb_config.c
openair2/GNB_APP/gnb_config.c
+10
-3
No files found.
ci-scripts/conf_files/gNB_SA_CU.conf
View file @
a2f41597
...
...
@@ -182,13 +182,13 @@ gNBs =
}
);
E1_INTERFACE
:
E1_INTERFACE
=
(
{
type
=
"
cu
"
;
ipv4_cucp
=
"1
92.168.100.1
"
;
type
=
"
up
"
;
ipv4_cucp
=
"1
27.0.0.100
"
;
port_cucp
=
25
;
ipv4_cuup
=
"1
92.168.100.2
"
;
ipv4_cuup
=
"1
27.0.0.101
"
;
port_cuup
=
25
;
}
)
...
...
common/utils/LOG/log.c
View file @
a2f41597
...
...
@@ -485,6 +485,7 @@ int logInit (void)
register_log_component
(
"SDAP"
,
""
,
SDAP
);
register_log_component
(
"S1AP"
,
""
,
S1AP
);
register_log_component
(
"F1AP"
,
""
,
F1AP
);
register_log_component
(
"E1AP"
,
""
,
E1AP
);
register_log_component
(
"M2AP"
,
""
,
M2AP
);
register_log_component
(
"M3AP"
,
""
,
M3AP
);
register_log_component
(
"SCTP"
,
""
,
SCTP
);
...
...
openair2/E1AP/e1ap.c
View file @
a2f41597
...
...
@@ -92,7 +92,7 @@ int e1ap_handle_message(instance_t instance, uint32_t assoc_id,
return
ret
;
}
void
cu
u
p_task_handle_sctp_data_ind
(
instance_t
instance
,
sctp_data_ind_t
*
sctp_data_ind
)
{
void
cu
x
p_task_handle_sctp_data_ind
(
instance_t
instance
,
sctp_data_ind_t
*
sctp_data_ind
)
{
int
result
;
DevAssert
(
sctp_data_ind
!=
NULL
);
e1ap_handle_message
(
instance
,
sctp_data_ind
->
assoc_id
,
...
...
@@ -205,7 +205,7 @@ int e1apCUUP_send_SETUP_REQUEST(instance_t instance) {
MCC_MNC_TO_PLMNID
(
setup
->
plmns
[
i
].
mcc
,
setup
->
plmns
[
i
].
mnc
,
setup
->
plmns
[
i
].
mnc_digit_length
,
&
supportedPLMN
->
pLMN_Identity
);
}
e1ap_encode_send
(
0
,
instance
,
&
pdu
,
0
,
__func__
);
e1ap_encode_send
(
UPtype
,
instance
,
&
pdu
,
0
,
__func__
);
return
0
;
}
...
...
@@ -228,7 +228,7 @@ int e1apCUCP_send_SETUP_RESPONSE(instance_t instance,
ieC1
->
value
.
present
=
E1AP_GNB_CU_UP_E1SetupResponseIEs__value_PR_TransactionID
;
ieC1
->
value
.
choice
.
TransactionID
=
e1ap_setup_resp
->
transac_id
;
e1ap_encode_send
(
0
,
instance
,
&
pdu
,
0
,
__func__
);
e1ap_encode_send
(
CPtype
,
instance
,
&
pdu
,
0
,
__func__
);
return
0
;
}
...
...
@@ -259,7 +259,7 @@ int e1apCUCP_send_SETUP_FAILURE(instance_t instance,
ieC2
->
value
.
choice
.
Cause
.
present
=
E1AP_Cause_PR_radioNetwork
;
//choose this accordingly
ieC2
->
value
.
choice
.
Cause
.
choice
.
radioNetwork
=
E1AP_CauseRadioNetwork_unspecified
;
e1ap_encode_send
(
0
,
instance
,
&
pdu
,
0
,
__func__
);
e1ap_encode_send
(
CPtype
,
instance
,
&
pdu
,
0
,
__func__
);
return
0
;
}
...
...
@@ -408,7 +408,7 @@ int e1apCUUP_send_CONFIGURATION_UPDATE(instance_t instance) {
TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING
(
1234
,
&
TNLAtoRemove
->
tNLAssociationTransportLayerAddress
.
choice
.
endpoint_IP_Address
);
// TODO: correct me
}
e1ap_encode_send
(
0
,
instance
,
&
pdu
,
0
,
__func__
);
e1ap_encode_send
(
UPtype
,
instance
,
&
pdu
,
0
,
__func__
);
return
0
;
}
...
...
@@ -632,7 +632,7 @@ int e1apCUCP_send_BEARER_CONTEXT_SETUP_REQUEST(instance_t instance,
}
}
e1ap_encode_send
(
0
,
instance
,
&
pdu
,
0
,
__func__
);
e1ap_encode_send
(
CPtype
,
instance
,
&
pdu
,
0
,
__func__
);
return
0
;
}
...
...
@@ -757,7 +757,7 @@ int e1apCUUP_send_BEARER_CONTEXT_SETUP_RESPONSE(instance_t instance,
}
}
}
e1ap_encode_send
(
0
,
instance
,
&
pdu
,
0
,
__func__
);
e1ap_encode_send
(
UPtype
,
instance
,
&
pdu
,
0
,
__func__
);
return
0
;
}
...
...
@@ -1116,6 +1116,27 @@ void cucp_task_send_sctp_init_req(instance_t instance, char *my_addr) {
itti_send_msg_to_task
(
TASK_SCTP
,
instance
,
message_p
);
}
void
cucp_task_handle_sctp_association_ind
(
instance_t
instance
,
sctp_new_association_ind_t
*
sctp_new_association_ind
)
{
createE1inst
(
CPtype
,
instance
,
NULL
);
e1ap_setup_req_t
*
setup_req
=
&
getCxtE1
(
CPtype
,
instance
)
->
setupReq
;
setup_req
->
assoc_id
=
sctp_new_association_ind
->
assoc_id
;
setup_req
->
sctp_in_streams
=
sctp_new_association_ind
->
in_streams
;
setup_req
->
sctp_out_streams
=
sctp_new_association_ind
->
out_streams
;
setup_req
->
default_sctp_stream_id
=
0
;
}
void
cucp_task_handle_sctp_association_resp
(
instance_t
instance
,
sctp_new_association_resp_t
*
sctp_new_association_resp
)
{
DevAssert
(
sctp_new_association_resp
!=
NULL
);
if
(
sctp_new_association_resp
->
sctp_state
!=
SCTP_STATE_ESTABLISHED
)
{
LOG_W
(
E1AP
,
"Received unsuccessful result for SCTP association (%u), instance %ld, cnx_id %u
\n
"
,
sctp_new_association_resp
->
sctp_state
,
instance
,
sctp_new_association_resp
->
ulp_cnx_id
);
return
;
}
}
void
*
E1AP_CUCP_task
(
void
*
arg
)
{
LOG_I
(
E1AP
,
"Starting E1AP at CU CP
\n
"
);
MessageDef
*
msg
=
NULL
;
...
...
@@ -1123,11 +1144,24 @@ void *E1AP_CUCP_task(void *arg) {
while
(
1
)
{
itti_receive_msg
(
TASK_CUCP_E1
,
&
msg
);
instance_t
myInstance
=
ITTI_MSG_DESTINATION_INSTANCE
(
msg
);
switch
(
ITTI_MSG_ID
(
msg
))
{
case
SCTP_NEW_ASSOCIATION_IND
:
LOG_I
(
E1AP
,
"CUCP Task Received SCTP_NEW_ASSOCIATION_IND for instance %ld
\n
"
,
myInstance
);
cucp_task_handle_sctp_association_ind
(
ITTI_MSG_ORIGIN_INSTANCE
(
msg
),
&
msg
->
ittiMsg
.
sctp_new_association_ind
);
break
;
case
SCTP_NEW_ASSOCIATION_RESP
:
LOG_I
(
E1AP
,
"CUCP Task Received SCTP_NEW_ASSOCIATION_RESP for instance %ld
\n
"
,
myInstance
);
cucp_task_handle_sctp_association_resp
(
ITTI_MSG_ORIGIN_INSTANCE
(
msg
),
&
msg
->
ittiMsg
.
sctp_new_association_resp
);
break
;
case
E1AP_SETUP_REQ
:
LOG_I
(
E1AP
,
"CUCP Task Received E1AP_SETUP_REQ for instance %ld. Initializing SCTP listener
\n
"
,
ITTI_MSG_DESTINATION_INSTANCE
(
msg
)
);
myInstance
);
e1ap_setup_req_t
*
req
=
&
E1AP_SETUP_REQ
(
msg
);
char
*
ipaddr
;
if
(
req
->
CUCP_e1_ip_address
.
ipv4
==
0
)
{
...
...
@@ -1139,6 +1173,11 @@ void *E1AP_CUCP_task(void *arg) {
cucp_task_send_sctp_init_req
(
0
,
ipaddr
);
break
;
case
SCTP_DATA_IND
:
LOG_I
(
E1AP
,
"CUCP Task Received SCTP_DATA_IND
\n
"
);
cuxp_task_handle_sctp_data_ind
(
myInstance
,
&
msg
->
ittiMsg
.
sctp_data_ind
);
break
;
default:
LOG_E
(
E1AP
,
"Unknown message received in TASK_CUCP_E1
\n
"
);
break
;
...
...
@@ -1172,7 +1211,7 @@ void *E1AP_CUUP_task(void *arg) {
case
SCTP_DATA_IND
:
LOG_I
(
E1AP
,
"CUUP Task Received SCTP_DATA_IND
\n
"
);
cu
u
p_task_handle_sctp_data_ind
(
myInstance
,
&
msg
->
ittiMsg
.
sctp_data_ind
);
cu
x
p_task_handle_sctp_data_ind
(
myInstance
,
&
msg
->
ittiMsg
.
sctp_data_ind
);
break
;
default:
...
...
openair2/E1AP/e1ap_common.c
View file @
a2f41597
...
...
@@ -34,18 +34,29 @@ e1ap_upcp_inst_t *getCxtE1(E1_t type, instance_t instance) {
return
type
?
e1ap_up_inst
[
instance
]
:
e1ap_cp_inst
[
instance
];
}
int
e1ap_assoc_id
(
bool
isCu
,
instance_t
instance
)
{
return
0
;
int
e1ap_assoc_id
(
E1_t
type
,
instance_t
instance
)
{
if
(
type
==
CPtype
)
{
AssertFatal
(
e1ap_cp_inst
[
instance
]
!=
NULL
,
"Trying to access uninitiated instance of CUCP
\n
"
);
return
e1ap_cp_inst
[
instance
]
->
setupReq
.
assoc_id
;
}
else
if
(
type
==
UPtype
)
{
AssertFatal
(
e1ap_up_inst
[
instance
]
!=
NULL
,
"Trying to access uninitiated instance of CUUP
\n
"
);
return
e1ap_up_inst
[
instance
]
->
setupReq
.
assoc_id
;
}
else
{
AssertFatal
(
false
,
"Unknown CU type
\n
"
);
}
return
-
1
;
}
void
createE1inst
(
E1_t
type
,
instance_t
instance
,
e1ap_setup_req_t
*
req
)
{
if
(
type
==
CPtype
)
{
AssertFatal
(
e1ap_cp_inst
[
instance
]
==
NULL
,
"Double call to E1 CP instance %d
\n
"
,
(
int
)
instance
);
e1ap_cp_inst
[
instance
]
=
(
e1ap_upcp_inst_t
*
)
calloc
(
1
,
sizeof
(
e1ap_upcp_inst_t
));
}
else
{
}
else
if
(
type
==
UPtype
)
{
AssertFatal
(
e1ap_up_inst
[
instance
]
==
NULL
,
"Double call to E1 UP instance %d
\n
"
,
(
int
)
instance
);
e1ap_up_inst
[
instance
]
=
(
e1ap_upcp_inst_t
*
)
calloc
(
1
,
sizeof
(
e1ap_upcp_inst_t
));
memcpy
(
&
e1ap_up_inst
[
instance
]
->
setupReq
,
req
,
sizeof
(
e1ap_setup_req_t
));
}
else
{
AssertFatal
(
false
,
"Unknown CU type
\n
"
);
}
}
...
...
@@ -76,7 +87,7 @@ E1AP_TransactionID_t E1AP_get_next_transaction_identifier() {
int
freeIdx
;
while
(
!
isTransacIdValid
)
{
genTransacId
=
rand
();
genTransacId
=
rand
()
&
255
;
isTransacIdValid
=
check_transac_id
(
genTransacId
,
&
freeIdx
);
}
...
...
@@ -143,7 +154,7 @@ int e1ap_decode_pdu(E1AP_E1AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t
asn_dec_rval_t
dec_ret
;
DevAssert
(
buffer
!=
NULL
);
dec_ret
=
aper_decode
(
NULL
,
&
asn_DEF_
F1AP_F
1AP_PDU
,
&
asn_DEF_
E1AP_E
1AP_PDU
,
(
void
**
)
&
pdu
,
buffer
,
length
,
...
...
@@ -151,9 +162,9 @@ int e1ap_decode_pdu(E1AP_E1AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t
0
);
if
(
asn1_xer_print_e1ap
)
{
LOG_E
(
F
1AP
,
"----------------- ASN1 DECODER PRINT START-----------------
\n
"
);
LOG_E
(
E
1AP
,
"----------------- ASN1 DECODER PRINT START-----------------
\n
"
);
xer_fprint
(
stdout
,
&
asn_DEF_E1AP_E1AP_PDU
,
pdu
);
LOG_E
(
F
1AP
,
"----------------- ASN1 DECODER PRINT END -----------------
\n
"
);
LOG_E
(
E
1AP
,
"----------------- ASN1 DECODER PRINT END -----------------
\n
"
);
}
if
(
dec_ret
.
code
!=
RC_OK
)
{
...
...
@@ -179,7 +190,7 @@ int e1ap_decode_pdu(E1AP_E1AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t
return
-
1
;
}
int
e1ap_encode_send
(
bool
isCu
,
instance_t
instance
,
E1AP_E1AP_PDU_t
*
pdu
,
uint16_t
stream
,
const
char
*
func
)
{
int
e1ap_encode_send
(
E1_t
type
,
instance_t
instance
,
E1AP_E1AP_PDU_t
*
pdu
,
uint16_t
stream
,
const
char
*
func
)
{
DevAssert
(
pdu
!=
NULL
);
if
(
asn1_xer_print_e1ap
)
{
...
...
@@ -197,15 +208,15 @@ int e1ap_encode_send(bool isCu, instance_t instance, E1AP_E1AP_PDU_t *pdu, uint1
}
void
*
buffer
=
NULL
;
ssize_t
encoded
=
aper_encode_to_new_buffer
(
&
asn_DEF_E1AP_E1AP_PDU
,
0
,
pdu
,
buffer
);
ssize_t
encoded
=
aper_encode_to_new_buffer
(
&
asn_DEF_E1AP_E1AP_PDU
,
0
,
pdu
,
&
buffer
);
if
(
encoded
<
0
)
{
LOG_E
(
E1AP
,
"%s: Failed to encode E1AP message
\n
"
,
func
);
return
-
1
;
}
else
{
MessageDef
*
message
=
itti_alloc_new_message
(
isCu
?
TASK_CUCP_E1
:
TASK_CUUP_E1
,
0
,
SCTP_DATA_REQ
);
MessageDef
*
message
=
itti_alloc_new_message
(
(
type
==
CPtype
)
?
TASK_CUCP_E1
:
TASK_CUUP_E1
,
0
,
SCTP_DATA_REQ
);
sctp_data_req_t
*
s
=
&
message
->
ittiMsg
.
sctp_data_req
;
s
->
assoc_id
=
e1ap_assoc_id
(
isCu
,
instance
);
s
->
assoc_id
=
e1ap_assoc_id
(
type
,
instance
);
s
->
buffer
=
buffer
;
s
->
buffer_length
=
encoded
;
s
->
stream
=
stream
;
...
...
openair2/E1AP/e1ap_common.h
View file @
a2f41597
...
...
@@ -36,9 +36,9 @@ void createE1inst(E1_t type, instance_t instance, e1ap_setup_req_t *req);
bool
check_transac_id
(
E1AP_TransactionID_t
id
,
int
*
freeIdx
);
int
e1ap_assoc_id
(
bool
isCu
,
instance_t
instance
);
int
e1ap_assoc_id
(
E1_t
type
,
instance_t
instance
);
int
e1ap_encode_send
(
bool
isCu
,
instance_t
instance
,
E1AP_E1AP_PDU_t
*
pdu
,
uint16_t
stream
,
const
char
*
func
);
int
e1ap_encode_send
(
E1_t
type
,
instance_t
instance
,
E1AP_E1AP_PDU_t
*
pdu
,
uint16_t
stream
,
const
char
*
func
);
void
e1ap_common_init
();
#endif
/* E1AP_COMMON_H_ */
openair2/GNB_APP/gnb_config.c
View file @
a2f41597
...
...
@@ -1835,6 +1835,8 @@ int RCconfig_NR_CU_E1(MessageDef *msg_p, uint32_t i) {
paramlist_def_t
GNBParamList
=
{
GNB_CONFIG_STRING_GNB_LIST
,
NULL
,
0
};
paramlist_def_t
GNBE1ParamList
=
{
GNB_CONFIG_STRING_E1_PARAMETERS
,
NULL
,
0
};
config_get
(
GNBSParams
,
sizeof
(
GNBSParams
)
/
sizeof
(
paramdef_t
),
NULL
);
char
aprefix
[
MAX_OPTNAME_SIZE
*
2
+
8
];
sprintf
(
aprefix
,
"%s.[%i]"
,
GNB_CONFIG_STRING_GNB_LIST
,
0
);
int
num_gnbs
=
GNBSParams
[
GNB_ACTIVE_GNBS_IDX
].
numelt
;
AssertFatal
(
i
<
num_gnbs
,
"Failed to parse config file no %uth element in %s
\n
"
,
i
,
GNB_CONFIG_STRING_ACTIVE_GNBS
);
...
...
@@ -1843,8 +1845,9 @@ int RCconfig_NR_CU_E1(MessageDef *msg_p, uint32_t i) {
config_getlist
(
&
GNBParamList
,
GNBParams
,
sizeof
(
GNBParams
)
/
sizeof
(
paramdef_t
),
NULL
);
AssertFatal
(
GNBParamList
.
paramarray
[
i
][
GNB_GNB_ID_IDX
].
uptr
!=
NULL
,
"gNB id %u is not defined in configuration file
\n
"
,
i
);
config_getlist
(
&
GNBE1ParamList
,
GNBE1Params
,
sizeof
(
GNBE1Params
)
/
sizeof
(
paramdef_t
),
NULL
);
config_getlist
(
&
GNBE1ParamList
,
GNBE1Params
,
sizeof
(
GNBE1Params
)
/
sizeof
(
paramdef_t
),
aprefix
);
e1ap_setup_req_t
*
e1Setup
=
&
E1AP_SETUP_REQ
(
msg_p
);
msg_p
->
ittiMsgHeader
.
destinationInstance
=
0
;
e1Setup
->
gNB_cu_up_id
=
*
(
GNBParamList
.
paramarray
[
0
][
GNB_GNB_ID_IDX
].
uptr
);
paramdef_t
PLMNParams
[]
=
GNBPLMNPARAMS_DESC
;
...
...
@@ -1855,7 +1858,7 @@ int RCconfig_NR_CU_E1(MessageDef *msg_p, uint32_t i) {
for
(
int
I
=
0
;
I
<
sizeof
(
PLMNParams
)
/
sizeof
(
paramdef_t
);
++
I
)
PLMNParams
[
I
].
chkPptr
=
&
(
config_check_PLMNParams
[
I
]);
config_getlist
(
&
PLMNParamList
,
PLMNParams
,
sizeof
(
PLMNParams
)
/
sizeof
(
paramdef_t
),
NULL
);
config_getlist
(
&
PLMNParamList
,
PLMNParams
,
sizeof
(
PLMNParams
)
/
sizeof
(
paramdef_t
),
aprefix
);
int
numPLMNs
=
PLMNParamList
.
numelt
;
e1Setup
->
supported_plmns
=
numPLMNs
;
...
...
@@ -1866,8 +1869,10 @@ int RCconfig_NR_CU_E1(MessageDef *msg_p, uint32_t i) {
}
strcpy
(
e1Setup
->
CUCP_e1_ip_address
.
ipv4_address
,
*
(
GNBE1ParamList
.
paramarray
[
0
][
GNB_CONFIG_E1_IPV4_ADDRESS_CUCP
].
strptr
));
e1Setup
->
CUCP_e1_ip_address
.
ipv4
=
1
;
e1Setup
->
port_cucp
=
*
GNBE1ParamList
.
paramarray
[
0
][
GNB_CONFIG_E1_PORT_CUCP
].
uptr
;
strcpy
(
e1Setup
->
CUUP_e1_ip_address
.
ipv4_address
,
*
(
GNBE1ParamList
.
paramarray
[
0
][
GNB_CONFIG_E1_IPV4_ADDRESS_CUUP
].
strptr
));
e1Setup
->
CUUP_e1_ip_address
.
ipv4
=
1
;
e1Setup
->
port_cuup
=
*
GNBE1ParamList
.
paramarray
[
0
][
GNB_CONFIG_E1_PORT_CUUP
].
uptr
;
e1Setup
->
cn_support
=
*
GNBE1ParamList
.
paramarray
[
0
][
GNB_CONFIG_E1_CN_SUPPORT
].
uptr
;
...
...
@@ -2314,7 +2319,9 @@ static ngran_node_t get_node_type(void)
config_getlist
(
&
MacRLC_ParamList
,
MacRLC_Params
,
sizeof
(
MacRLC_Params
)
/
sizeof
(
paramdef_t
),
NULL
);
config_getlist
(
&
GNBParamList
,
GNBParams
,
sizeof
(
GNBParams
)
/
sizeof
(
paramdef_t
),
NULL
);
config_getlist
(
&
GNBE1ParamList
,
GNBE1Params
,
sizeof
(
GNBE1Params
)
/
sizeof
(
paramdef_t
),
NULL
);
char
aprefix
[
MAX_OPTNAME_SIZE
*
2
+
8
];
sprintf
(
aprefix
,
"%s.[%i]"
,
GNB_CONFIG_STRING_GNB_LIST
,
0
);
config_getlist
(
&
GNBE1ParamList
,
GNBE1Params
,
sizeof
(
GNBE1Params
)
/
sizeof
(
paramdef_t
),
aprefix
);
if
(
MacRLC_ParamList
.
numelt
>
0
)
{
RC
.
nb_nr_macrlc_inst
=
MacRLC_ParamList
.
numelt
;
...
...
FANG WANG
@wf
mentioned in commit
553f1d1a
·
Feb 11, 2023
mentioned in commit
553f1d1a
mentioned in commit 553f1d1a52845911be4ba61ec2bcc5c60ad42ae7
Toggle commit list
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