Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-AMF
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
1
Issues
1
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
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-AMF
Commits
6568d1d7
Commit
6568d1d7
authored
Apr 26, 2021
by
yangjian
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update test
parent
c84bc571
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
140 additions
and
75 deletions
+140
-75
src/amf-app/amf_app.cpp
src/amf-app/amf_app.cpp
+5
-5
src/amf-app/amf_app.hpp
src/amf-app/amf_app.hpp
+1
-1
src/amf-app/amf_n1.cpp
src/amf-app/amf_n1.cpp
+39
-10
src/contexts/ue_ngap_context.hpp
src/contexts/ue_ngap_context.hpp
+1
-1
src/itti/itti_msg.hpp
src/itti/itti_msg.hpp
+1
-1
src/itti/msgs/itti_msg_amf_app.hpp
src/itti/msgs/itti_msg_amf_app.hpp
+4
-4
src/nas/msgs/DeregistrationRequest.cpp
src/nas/msgs/DeregistrationRequest.cpp
+6
-6
src/ngap/ngapIEs/FiveGSTmsi.cpp
src/ngap/ngapIEs/FiveGSTmsi.cpp
+2
-1
src/ngap/ngapIEs/FiveGSTmsi.hpp
src/ngap/ngapIEs/FiveGSTmsi.hpp
+1
-0
src/sbi/amf_server/api/N1N2MessageCollectionDocumentApi.cpp
src/sbi/amf_server/api/N1N2MessageCollectionDocumentApi.cpp
+1
-6
src/sbi/amf_server/api/TestSignallingApi.cpp
src/sbi/amf_server/api/TestSignallingApi.cpp
+27
-4
src/sbi/amf_server/api/TestSignallingApi.h
src/sbi/amf_server/api/TestSignallingApi.h
+11
-5
src/sbi/amf_server/impl/N1N2MessageCollectionDocumentApiImpl.cpp
.../amf_server/impl/N1N2MessageCollectionDocumentApiImpl.cpp
+0
-29
src/sbi/amf_server/impl/TestSignallingApiImpl.cpp
src/sbi/amf_server/impl/TestSignallingApiImpl.cpp
+37
-1
src/sbi/amf_server/impl/TestSignallingApiImpl.h
src/sbi/amf_server/impl/TestSignallingApiImpl.h
+4
-1
No files found.
src/amf-app/amf_app.cpp
View file @
6568d1d7
...
...
@@ -111,10 +111,10 @@ void amf_app_task(void*) {
amf_app_inst
->
handle_itti_message
(
ref
(
*
m
));
}
break
;
case
PAGING_N1N2_MESSAGE_TRANSFER
:
{
Logger
::
amf_app
().
debug
(
"Received
PAGING_N1N2_MESSAGE_TRANSFER
"
);
itti_
paging_n1n2_message_transfer
*
m
=
dynamic_cast
<
itti_
paging_n1n2_message_transfer
*>
(
msg
);
case
TEST_SIGNALLING_PAGING
:
{
Logger
::
amf_app
().
debug
(
"Received
TEST_SIGNALLING_PAGING
"
);
itti_
test_signalling_paging
*
m
=
dynamic_cast
<
itti_
test_signalling_paging
*>
(
msg
);
amf_app_inst
->
handle_itti_message
(
ref
(
*
m
));
}
break
;
...
...
@@ -197,7 +197,7 @@ void amf_app::set_ran_amf_id_2_ue_context(
// ITTI handlers
//------------------------------------------------------------------------------
void
amf_app
::
handle_itti_message
(
itti_
paging_n1n2_message_transfer
&
itti_msg
)
{
itti_
test_signalling_paging
&
itti_msg
)
{
itti_paging
*
paging_msg
=
new
itti_paging
(
TASK_AMF_APP
,
TASK_AMF_N2
);
...
...
src/amf-app/amf_app.hpp
View file @
6568d1d7
...
...
@@ -55,7 +55,7 @@ class amf_app {
// itti handlers
void
handle_itti_message
(
itti_nas_signalling_establishment_request
&
itti_msg
);
void
handle_itti_message
(
itti_n1n2_message_transfer_request
&
itti_msg
);
void
handle_itti_message
(
itti_
paging_n1n2_message_transfer
&
itti_msg
);
void
handle_itti_message
(
itti_
test_signalling_paging
&
itti_msg
);
void
handle_itti_message
(
itti_test_signalling_network_initiated_deregistration
&
itti_msg
);
// context management
std
::
map
<
long
,
std
::
shared_ptr
<
ue_context
>>
amf_ue_ngap_id2ue_ctx
;
...
...
src/amf-app/amf_n1.cpp
View file @
6568d1d7
...
...
@@ -238,6 +238,7 @@ void amf_n1::handle_itti_message(itti_uplink_nas_data_ind &nas_data_ind) {
}
else
{
if
(
is_amf_ue_id_2_nas_context
(
amf_ue_ngap_id
))
nc
=
amf_ue_id_2_nas_context
(
amf_ue_ngap_id
);
else
Logger
::
amf_n1
().
warn
(
"No existing nas_context with amf_ue_ngap_id 0x%x"
,
amf_ue_ngap_id
);
...
...
@@ -635,6 +636,8 @@ void amf_n1::service_request_handle(bool isNasSig,
delete
serApt
;
return
;
}
psc
.
get
()
->
ran_ue_ngap_id
=
ran_ue_ngap_id
;
psc
.
get
()
->
amf_ue_ngap_id
=
amf_ue_ngap_id
;
}
else
{
Logger
::
amf_n1
().
error
(
"Cannot get pdu_session_context with SUPI %s"
,
supi
.
c_str
());
...
...
@@ -993,14 +996,20 @@ void amf_n1::registration_request_handle(bool isNasSig,
run_periodic_registration_update_procedure
(
nc
,
pdu_session_status
);
}
break
;
case
PERIODIC_REGISTRATION_UPDATING
:
{
// Logger::amf_n1().error(
// "Network doesn't support periodic registration, reject ...");
Logger
::
amf_n1
().
debug
(
"Network handling periodic registration ..."
);
if
(
is_messagecontainer
)
run_periodic_registration_update_procedure
(
nc
,
nas_msg
);
else
run_periodic_registration_update_procedure
(
nc
,
pdu_session_status
);
char
*
pathvar
=
NULL
;
pathvar
=
getenv
(
"IS_AMF_PERIODIC"
);
if
(
pathvar
&&
!
strcmp
(
pathvar
,
"true"
))
{
Logger
::
amf_n1
().
debug
(
"Network handling periodic registration ..."
);
if
(
is_messagecontainer
)
run_periodic_registration_update_procedure
(
nc
,
nas_msg
);
else
run_periodic_registration_update_procedure
(
nc
,
pdu_session_status
);
}
else
{
Logger
::
amf_n1
().
warn
(
"Please set environment variable
\"
IS_AMF_PERIODIC
\"
"
);
Logger
::
amf_n1
().
error
(
"Network doesn't support periodic registration, reject ..."
);
}
}
break
;
case
EMERGENCY_REGISTRATION
:
{
if
(
!
amf_cfg
.
is_emergency_support
.
compare
(
"false"
))
{
...
...
@@ -2128,7 +2137,22 @@ void amf_n1::security_mode_complete_handle(uint32_t ran_ue_ngap_id,
// TODO: remove hardcoded values
regAccept
->
set_5GS_Network_Feature_Support
(
0x01
,
0x00
);
regAccept
->
setT3512_Value
(
0x5
,
0x01
);
char
*
pathvar
=
NULL
;
pathvar
=
getenv
(
"AMF_T3512_MIN"
);
uint8_t
t3512_Value
=
0x1e
;
if
(
pathvar
)
{
uint8_t
value
=
atoi
(
pathvar
);
if
(
value
)
{
t3512_Value
=
value
;
}
}
else
{
Logger
::
amf_n1
().
warn
(
"Please set environment variable
\"
AMF_T3512_MIN
\"
"
);
}
regAccept
->
setT3512_Value
(
0x5
,
t3512_Value
);
uint8_t
buffer
[
BUFFER_SIZE_1024
]
=
{
0
};
int
encoded_size
=
regAccept
->
encode2buffer
(
buffer
,
BUFFER_SIZE_1024
);
print_buffer
(
"amf_n1"
,
"Registration-Accept message buffer"
,
buffer
,
...
...
@@ -2558,6 +2582,7 @@ void amf_n1::network_initiate_de_registration_handle(uint32_t ran_ue_ngap_id,
// encode NAS msg
DeregistrationRequest
*
deregReq
=
new
DeregistrationRequest
();
deregReq
->
setHeader
(
PLAIN_5GS_MSG
,
DEREGISTRATION_REQUEST_UE_TERMINATED
);
//deregReq->setngKSI(NAS_KEY_SET_IDENTIFIER_NATIVE,nc.get()->ngKsi);
deregReq
->
setDeregistrationType
(
0x05
);
...
...
@@ -2571,8 +2596,12 @@ void amf_n1::network_initiate_de_registration_handle(uint32_t ran_ue_ngap_id,
return
;
}
bstring
b
=
blk2bstr
(
buffer
,
encoded_size
);
itti_send_dl_nas_buffer_to_task_n2
(
b
,
ran_ue_ngap_id
,
amf_ue_ngap_id
);
bstring
intProtctedNas
;
encode_nas_message_protected
(
nc
.
get
()
->
security_ctx
,
false
,
INTEGRITY_PROTECTED_AND_CIPHERED
,
NAS_MESSAGE_DOWNLINK
,
buffer
,
encoded_size
,
intProtctedNas
);
itti_send_dl_nas_buffer_to_task_n2
(
intProtctedNas
,
ran_ue_ngap_id
,
amf_ue_ngap_id
);
set_5gmm_state
(
nc
,
_5GMM_DEREGISTERED
);
if
(
nc
.
get
()
->
is_stacs_available
)
{
...
...
src/contexts/ue_ngap_context.hpp
View file @
6568d1d7
...
...
@@ -56,7 +56,7 @@ class ue_ngap_context {
bool
ueContextRequest
;
uint32_t
s_tmsi_5g
;
std
::
string
s_setid
=
"
1
"
;
std
::
string
s_setid
=
"
4
"
;
std
::
string
s_pointer
=
"1"
;
std
::
string
s_tmsi
=
"1"
;
...
...
src/itti/itti_msg.hpp
View file @
6568d1d7
...
...
@@ -82,7 +82,7 @@ typedef enum {
HANDOVER_NOTIFY
,
UPLINKRANSTATUSTRANSFER
,
PDU_SESS_RES_SET_RESP
,
PAGING_N1N2_MESSAGE_TRANSFER
,
TEST_SIGNALLING_PAGING
,
TEST_SIGNALLING_NETWORK_INITIATED_DEREGISTRAATION
,
PAGING
,
TIME_OUT
,
...
...
src/itti/msgs/itti_msg_amf_app.hpp
View file @
6568d1d7
...
...
@@ -60,11 +60,11 @@ class itti_n1n2_message_transfer_request : public itti_msg_amf_app {
// other parameters
};
class
itti_
paging_n1n2_message_transfer
:
public
itti_msg_amf_app
{
class
itti_
test_signalling_paging
:
public
itti_msg_amf_app
{
public:
itti_
paging_n1n2_message_transfer
(
const
task_id_t
origin
,
const
task_id_t
destination
)
:
itti_msg_amf_app
(
PAGING_N1N2_MESSAGE_TRANSFER
,
origin
,
destination
)
{}
itti_
paging_n1n2_message_transfer
(
const
itti_paging_n1n2_message_transfer
&
i
)
:
itti_msg_amf_app
(
i
)
{}
itti_
test_signalling_paging
(
const
task_id_t
origin
,
const
task_id_t
destination
)
:
itti_msg_amf_app
(
TEST_SIGNALLING_PAGING
,
origin
,
destination
)
{}
itti_
test_signalling_paging
(
const
itti_test_signalling_paging
&
i
)
:
itti_msg_amf_app
(
i
)
{}
public:
uint32_t
ran_ue_ngap_id
;
...
...
src/nas/msgs/DeregistrationRequest.cpp
View file @
6568d1d7
...
...
@@ -181,9 +181,9 @@ int DeregistrationRequest::encode2buffer(uint8_t* buf, int len) {
encoded_size
+=
3
;
if
(
!
(
ie_deregistrationtype
->
encode2buffer
(
buf
+
encoded_size
,
len
-
encoded_size
)))
{
if
(
plain_header
->
getMessageType
()
==
DEREGISTRATION_REQUEST_UE_ORIGINATING
)
if
(
ie_ngKSI
!=
NULL
)
{
if
(
!
(
ie_ngKSI
->
encode2buffer
(
buf
+
encoded_size
,
len
-
encoded_size
)
))
{
if
(
ie_ngKSI
->
encode2buffer
(
buf
+
encoded_size
,
len
-
encoded_size
))
{
encoded_size
+=
1
;
}
else
{
Logger
::
nas_mm
().
error
(
"Encoding IE ie_ngKSI error"
);
...
...
@@ -208,10 +208,10 @@ int DeregistrationRequest::encode2buffer(uint8_t* buf, int len) {
}
}
*
(
buf
+
encoded_size
)
=
0x58
;
encoded_size
+=
1
;
*
(
buf
+
encoded_size
)
=
74
;
encoded_size
+=
1
;
//
*(buf + encoded_size) = 0x58;
//
encoded_size += 1;
// *(buf + encoded_size) = 6
;
//
encoded_size += 1;
Logger
::
nas_mm
().
debug
(
"Encoded DeregistrationRequest message len (%d)"
,
encoded_size
);
...
...
src/ngap/ngapIEs/FiveGSTmsi.cpp
View file @
6568d1d7
...
...
@@ -46,6 +46,7 @@ bool FiveGSTmsi::decodefrompdu(Ngap_FiveG_S_TMSI_t pdu) {
amfSetid
.
getAMFSetID
(
setId
);
amfPointer
.
getAMFPointer
(
pointer
);
_5g_s_tmsi
=
setId
+
pointer
+
std
::
to_string
(
tmsi
);
tmsi_value
=
std
::
to_string
(
tmsi
);
return
true
;
}
...
...
@@ -59,7 +60,7 @@ void FiveGSTmsi::getValue(std::string& setid,std::string& pointer,std::string& t
{
amfSetid
.
getAMFSetID
(
setid
);
amfPointer
.
getAMFPointer
(
pointer
);
tmsi
=
_5g_s_tmsi
;
tmsi
=
tmsi_value
;
}
...
...
src/ngap/ngapIEs/FiveGSTmsi.hpp
View file @
6568d1d7
...
...
@@ -52,6 +52,7 @@ class FiveGSTmsi {
private:
std
::
string
_5g_s_tmsi
;
std
::
string
tmsi_value
;
AMFSetID
amfSetid
;
AMFPointer
amfPointer
;
...
...
src/sbi/amf_server/api/N1N2MessageCollectionDocumentApi.cpp
View file @
6568d1d7
...
...
@@ -92,11 +92,6 @@ void N1N2MessageCollectionDocumentApi::n1_n2_message_transfer_handler(
try
{
nlohmann
::
json
::
parse
(
parts
[
0
].
body
.
c_str
())
.
get_to
(
n1N2MessageTransferReqData
);
if
(
n1N2MessageTransferReqData
.
ppiIsSet
())
{
this
->
n1_n2_message_transfer
(
ueContextId
,
n1N2MessageTransferReqData
,
response
);
}
else
{
if
(
!
is_ngap
)
this
->
n1_n2_message_transfer
(
ueContextId
,
n1N2MessageTransferReqData
,
parts
[
1
].
body
,
response
);
...
...
@@ -104,7 +99,7 @@ void N1N2MessageCollectionDocumentApi::n1_n2_message_transfer_handler(
this
->
n1_n2_message_transfer
(
ueContextId
,
n1N2MessageTransferReqData
,
parts
[
1
].
body
,
parts
[
2
].
body
,
response
);
}
}
catch
(
nlohmann
::
detail
::
exception
&
e
)
{
// send a 400 error
Logger
::
amf_server
().
error
(
...
...
src/sbi/amf_server/api/TestSignallingApi.cpp
View file @
6568d1d7
...
...
@@ -21,10 +21,11 @@ void TestSignallingApi::
Routes
::
Post
(
*
router
,
base
+
"/test-signalling/network-initiated-deregistration/:subscriptionId"
,
Routes
::
bind
(
&
TestSignallingApi
::
test_signalling_network_initiated_deregistration_handler
,
this
));
Routes
::
bind
(
&
TestSignallingApi
::
test_signalling_network_initiated_deregistration_handler
,
this
));
Routes
::
Post
(
*
router
,
base
+
"/test-signalling/paging/:ueContextId"
,
Routes
::
bind
(
&
TestSignallingApi
::
test_signalling_paging_handler
,
this
));
// Default handler, called when a route is not found
router
->
addCustomHandler
(
Routes
::
bind
(
...
...
@@ -52,7 +53,29 @@ void TestSignallingApi::
response
.
send
(
Pistache
::
Http
::
Code
::
Internal_Server_Error
,
e
.
what
());
return
;
}
}
void
TestSignallingApi
::
test_signalling_paging_handler
(
const
Pistache
::
Rest
::
Request
&
request
,
Pistache
::
Http
::
ResponseWriter
response
)
{
// Getting the path params
auto
ueContextId
=
request
.
param
(
":ueContextId"
).
as
<
std
::
string
>
();
try
{
this
->
test_signalling_paging
(
ueContextId
,
response
);
}
catch
(
nlohmann
::
detail
::
exception
&
e
)
{
// send a 400 error
response
.
send
(
Pistache
::
Http
::
Code
::
Bad_Request
,
e
.
what
());
return
;
}
catch
(
std
::
exception
&
e
)
{
// send a 500 error
response
.
send
(
Pistache
::
Http
::
Code
::
Internal_Server_Error
,
e
.
what
());
return
;
}
}
void
TestSignallingApi
::
test_signalling_api_default_handler
(
...
...
src/sbi/amf_server/api/TestSignallingApi.h
View file @
6568d1d7
...
...
@@ -29,9 +29,11 @@ class TestSignallingApi {
void
test_signalling_network_initiated_deregistration_handler
(
const
Pistache
::
Rest
::
Request
&
request
,
Pistache
::
Http
::
ResponseWriter
response
);
void
test_signalling_api_default_handler
(
Pistache
::
Http
::
ResponseWriter
response
);
void
test_signalling_paging_handler
(
const
Pistache
::
Rest
::
Request
&
request
,
Pistache
::
Http
::
ResponseWriter
response
);
void
test_signalling_api_default_handler
(
const
Pistache
::
Rest
::
Request
&
request
,
Pistache
::
Http
::
ResponseWriter
response
);
...
...
@@ -40,8 +42,12 @@ class TestSignallingApi {
virtual
void
test_signalling_network_initiated_deregistration
(
const
std
::
string
&
subscriptionId
,
Pistache
::
Http
::
ResponseWriter
&
response
)
=
0
;
};
Pistache
::
Http
::
ResponseWriter
&
response
)
=
0
;
virtual
void
test_signalling_paging
(
const
std
::
string
&
ueContextId
,
Pistache
::
Http
::
ResponseWriter
&
response
)
=
0
;
};
}
// namespace api
}
// namespace amf
...
...
src/sbi/amf_server/impl/N1N2MessageCollectionDocumentApiImpl.cpp
View file @
6568d1d7
...
...
@@ -55,35 +55,6 @@ void N1N2MessageCollectionDocumentApiImpl::n1_n2_message_transfer(
Pistache
::
Http
::
Code
::
Ok
,
"N1N2MessageCollectionDocumentApiImpl::n1_n2_message_transfer API has "
"not been implemented yet!"
);
std
::
string
supi
=
ueContextId
;
Logger
::
amf_server
().
debug
(
"Key for PDU Session context: SUPI (%s)"
,
supi
.
c_str
());
std
::
shared_ptr
<
pdu_session_context
>
psc
;
if
(
amf_n11_inst
->
is_supi_to_pdu_ctx
(
supi
))
{
psc
=
amf_n11_inst
->
supi_to_pdu_ctx
(
supi
);
itti_paging_n1n2_message_transfer
*
itti_msg
=
new
itti_paging_n1n2_message_transfer
(
AMF_SERVER
,
TASK_AMF_APP
);
itti_msg
->
ran_ue_ngap_id
=
psc
.
get
()
->
ran_ue_ngap_id
;
itti_msg
->
amf_ue_ngap_id
=
psc
.
get
()
->
amf_ue_ngap_id
;
itti_msg
->
plmn
=
psc
.
get
()
->
plmn
;
std
::
shared_ptr
<
itti_paging_n1n2_message_transfer
>
i
=
std
::
shared_ptr
<
itti_paging_n1n2_message_transfer
>
(
itti_msg
);
int
ret
=
itti_inst
->
send_msg
(
i
);
if
(
0
!=
ret
)
{
Logger
::
amf_server
().
error
(
"Could not send ITTI message %s to task TASK_AMF_APP"
,
i
->
get_msg_name
());
}
}
else
{
Logger
::
amf_server
().
error
(
"Cannot get pdu_session_context with SUPI (%s)"
,
supi
.
c_str
());
}
}
...
...
src/sbi/amf_server/impl/TestSignallingApiImpl.cpp
View file @
6568d1d7
...
...
@@ -55,7 +55,43 @@ void TestSignallingApiImpl::
Logger
::
amf_server
().
error
(
"Cannot get pdu_session_context with SUPI (%s)"
,
supi
.
c_str
());
}
}
}
void
TestSignallingApiImpl
::
test_signalling_paging
(
const
std
::
string
&
ueContextId
,
Pistache
::
Http
::
ResponseWriter
&
response
)
{
response
.
send
(
Pistache
::
Http
::
Code
::
Ok
,
"test_signalling_paging ok!
\n
"
);
std
::
string
supi
=
ueContextId
;
Logger
::
amf_server
().
debug
(
"Key for PDU Session context: SUPI (%s)"
,
supi
.
c_str
());
std
::
shared_ptr
<
pdu_session_context
>
psc
;
if
(
amf_n11_inst
->
is_supi_to_pdu_ctx
(
supi
))
{
psc
=
amf_n11_inst
->
supi_to_pdu_ctx
(
supi
);
itti_test_signalling_paging
*
itti_msg
=
new
itti_test_signalling_paging
(
AMF_SERVER
,
TASK_AMF_APP
);
itti_msg
->
ran_ue_ngap_id
=
psc
.
get
()
->
ran_ue_ngap_id
;
itti_msg
->
amf_ue_ngap_id
=
psc
.
get
()
->
amf_ue_ngap_id
;
itti_msg
->
plmn
=
psc
.
get
()
->
plmn
;
std
::
shared_ptr
<
itti_test_signalling_paging
>
i
=
std
::
shared_ptr
<
itti_test_signalling_paging
>
(
itti_msg
);
int
ret
=
itti_inst
->
send_msg
(
i
);
if
(
0
!=
ret
)
{
Logger
::
amf_server
().
error
(
"Could not send ITTI message %s to task TASK_AMF_APP"
,
i
->
get_msg_name
());
}
}
else
{
Logger
::
amf_server
().
error
(
"Cannot get pdu_session_context with SUPI (%s)"
,
supi
.
c_str
());
}
}
}
// namespace api
}
// namespace amf
...
...
src/sbi/amf_server/impl/TestSignallingApiImpl.h
View file @
6568d1d7
...
...
@@ -30,7 +30,10 @@ class TestSignallingApiImpl
void
test_signalling_network_initiated_deregistration
(
const
std
::
string
&
subscriptionId
,
Pistache
::
Http
::
ResponseWriter
&
response
);
Pistache
::
Http
::
ResponseWriter
&
response
);
void
test_signalling_paging
(
const
std
::
string
&
ueContextId
,
Pistache
::
Http
::
ResponseWriter
&
response
);
private:
amf_application
::
amf_app
*
m_amf_app
;
...
...
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