Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-SMF
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
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-SMF
Commits
35961b90
Commit
35961b90
authored
Nov 04, 2022
by
Stefan Spettel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(smf): Added UL CL support in Downlink direction
Signed-off-by:
Stefan Spettel
<
stefan.spettel@eurecom.fr
>
parent
86f119a0
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
224 additions
and
93 deletions
+224
-93
src/smf_app/smf_pfcp_association.cpp
src/smf_app/smf_pfcp_association.cpp
+25
-0
src/smf_app/smf_procedure.cpp
src/smf_app/smf_procedure.cpp
+182
-86
src/smf_app/smf_procedure.hpp
src/smf_app/smf_procedure.hpp
+17
-7
No files found.
src/smf_app/smf_pfcp_association.cpp
View file @
35961b90
...
...
@@ -1097,6 +1097,31 @@ void upf_graph::dfs_next_upf(
std
::
make_shared
<
smf_qos_flow
>
(
qos_flow_asynch
);
edge_it
.
qos_flows
.
emplace_back
(
flow
);
}
// TODO thought for refactor: It would be much nicer if it would be the
// same edge object so we dont have to do that
// pointer is not null -> N9 interface
if
(
edge_it
.
association
)
{
// we add the TEID here for the edge in the other direction
// direct access is safe as we know the edge exists
auto
edge_node
=
adjacency_list
[
edge_it
.
association
];
for
(
auto
edge_edge
:
edge_node
)
{
if
(
edge_edge
.
qos_flows
.
empty
())
{
edge_edge
.
qos_flows
.
emplace_back
(
std
::
make_shared
<
smf_qos_flow
>
(
qos_flow_asynch
));
}
if
(
edge_edge
.
association
&&
edge_edge
.
association
==
node_it
->
first
)
{
if
(
edge_edge
.
type
==
iface_type
::
N9
&&
edge_edge
.
uplink
)
{
// downlink direction
edge_it
.
qos_flows
[
0
]
->
dl_fteid
=
edge_edge
.
qos_flows
[
0
]
->
dl_fteid
;
}
else
if
(
edge_edge
.
type
==
iface_type
::
N9
)
{
edge_it
.
qos_flows
[
0
]
->
ul_fteid
=
edge_edge
.
qos_flows
[
0
]
->
ul_fteid
;
}
}
}
}
// set the correct edges to return
if
(
edge_it
.
uplink
)
{
...
...
src/smf_app/smf_procedure.cpp
View file @
35961b90
...
...
@@ -271,9 +271,10 @@ pfcp::create_pdr smf_session_procedure::pfcp_create_pdr(
if
(
edge
.
type
!=
iface_type
::
N6
)
{
// in UPLINK always choose ID
if
(
edge
.
uplink
)
{
local_fteid
=
pfcp_prepare_fteid
(
flow
->
ul_fteid
);
local_fteid
=
pfcp_prepare_fteid
(
flow
->
dl_fteid
);
local_fteid
.
chid
=
0
;
}
else
{
local_fteid
=
pfcp_prepare_fteid
(
flow
->
d
l_fteid
);
local_fteid
=
pfcp_prepare_fteid
(
flow
->
u
l_fteid
);
}
pdi
.
set
(
local_fteid
);
}
...
...
@@ -725,8 +726,8 @@ smf_procedure_code session_create_sm_context_procedure::handle_itti_msg(
send_n4
=
true
;
for
(
auto
ul_edge
:
next_ul_edges
)
{
auto
flow
=
ul_edge
.
get_qos_flow
(
current_flow
.
qfi
);
if
(
ul_edge
.
association
&&
ul_edge
.
association
==
current_upf
)
{
flow
->
ul_fteid
=
default_qos_flow
->
ul_fteid
;
if
(
!
flow
)
{
send_n4
=
false
;
}
if
(
ul_edge
.
type
==
iface_type
::
N9
&&
flow
->
ul_fteid
.
is_zero
())
{
Logger
::
smf_app
().
debug
(
...
...
@@ -782,6 +783,61 @@ smf_procedure_code session_create_sm_context_procedure::handle_itti_msg(
return
smf_procedure_code
::
OK
;
}
//------------------------------------------------------------------------------
smf_procedure_code
session_update_sm_context_procedure
::
send_n4_session_modification_request
()
{
std
::
shared_ptr
<
pfcp_association
>
current_upf
{};
std
::
vector
<
edge
>
dl_edges
{};
std
::
vector
<
edge
>
ul_edges
{};
if
(
get_current_upf
(
dl_edges
,
ul_edges
,
current_upf
)
!=
smf_procedure_code
::
OK
)
{
return
smf_procedure_code
::
ERROR
;
}
n4_triggered
=
std
::
make_shared
<
itti_n4_session_modification_request
>
(
TASK_SMF_APP
,
TASK_SMF_N4
);
n4_triggered
->
seid
=
sps
->
up_fseid
.
seid
;
n4_triggered
->
trxn_id
=
this
->
trxn_id
;
n4_triggered
->
r_endpoint
=
endpoint
(
current_upf
->
node_id
.
u1
.
ipv4_address
,
pfcp
::
default_port
);
edge
dl_edge
=
dl_edges
[
0
];
for
(
auto
&
ul_edge
:
ul_edges
)
{
// we set PDR ID UL to 0, so we create new ones
auto
flow_dl
=
dl_edge
.
get_qos_flow
(
current_flow
.
qfi
);
auto
ul_flow
=
ul_edge
.
get_qos_flow
(
current_flow
.
qfi
);
if
(
!
flow_dl
||
!
ul_flow
)
{
Logger
::
smf_app
().
error
(
"Could not get the QOS Flow for DL procedure"
);
continue
;
}
flow_dl
->
pdr_id_dl
=
0
;
pfcp
::
create_far
create_far
=
pfcp_create_far
(
dl_edge
,
current_flow
.
qfi
);
ul_flow
->
far_id_dl
=
create_far
.
far_id
;
pfcp
::
create_pdr
create_pdr
=
pfcp_create_pdr
(
ul_edge
,
current_flow
.
qfi
);
// Add IEs to message
n4_triggered
->
pfcp_ies
.
set
(
create_far
);
n4_triggered
->
pfcp_ies
.
set
(
create_pdr
);
}
synch_ul_dl_edges
(
dl_edges
,
ul_edges
,
current_flow
.
qfi
);
Logger
::
smf_app
().
info
(
"Sending ITTI message %s to task TASK_SMF_N4"
,
n4_triggered
->
get_msg_name
());
int
ret
=
itti_inst
->
send_msg
(
n4_triggered
);
if
(
RETURNok
!=
ret
)
{
Logger
::
smf_app
().
error
(
"Could not send ITTI message %s to task TASK_SMF_N4"
,
n4_triggered
->
get_msg_name
());
return
smf_procedure_code
::
ERROR
;
}
return
smf_procedure_code
::
CONTINUE
;
}
//------------------------------------------------------------------------------
smf_procedure_code
session_update_sm_context_procedure
::
run
(
std
::
shared_ptr
<
itti_n11_update_sm_context_request
>
sm_context_req
,
...
...
@@ -828,24 +884,21 @@ smf_procedure_code session_update_sm_context_procedure::run(
return
smf_procedure_code
::
ERROR
;
}
std
::
vector
<
edge
>
dl_edges
;
std
::
vector
<
edge
>
ul_edges
;
std
::
shared_ptr
<
pfcp_association
>
current_upf
;
// TODO: UPF insertion in case of Handover
graph
->
dfs_current_upf
(
dl_edges
,
ul_edges
,
current_upf
);
// The flow is not needed anymore in DL direction, as we already have flows
smf_qos_flow
empty_flow
{};
graph
->
start_asynch_dfs_procedure
(
false
,
empty_flow
);
// smf_qos_flow empty_flow
;
// graph->start_asynch_dfs_procedure(false, empty_flow)
;
// TODO: UPF insertion in case of Handover
std
::
shared_ptr
<
pfcp_association
>
current_upf
;
std
::
vector
<
edge
>
dl_edges
;
std
::
vector
<
edge
>
ul_edges
;
/* if (not pfcp_associations::get_instance().select_up_node(
up_node_id, NODE_SELECTION_CRITERIA_MIN_PFCP_SESSIONS)) {
sm_context_resp->res.set_cause(
PDU_SESSION_APPLICATION_ERROR_PEER_NOT_RESPONDING);
Logger::smf_app().info("[SMF Procedure] REMOTE_PEER_NOT_RESPONDING");
return RETURNerror;
}
*/
if
(
get_next_upf
(
dl_edges
,
ul_edges
,
current_upf
)
!=
smf_procedure_code
::
CONTINUE
)
{
Logger
::
smf_app
().
error
(
"DL Procedure Error: No UPF to select"
);
return
smf_procedure_code
::
ERROR
;
}
//-------------------
n11_trigger
=
sm_context_req
;
...
...
@@ -868,6 +921,8 @@ smf_procedure_code session_update_sm_context_procedure::run(
for
(
auto
i
:
list_of_qfis_to_be_modified
)
{
Logger
::
smf_app
().
debug
(
"QoS Flow to be modified (QFI %d)"
,
i
.
qfi
);
// TODO currently just support one flow
current_flow
.
qfi
=
i
;
}
Logger
::
smf_app
().
debug
(
...
...
@@ -891,7 +946,7 @@ smf_procedure_code session_update_sm_context_procedure::run(
case
session_management_procedures_type_e
:
:
N2_HO_PREPARATION_PHASE_STEP2
:
{
pfcp
::
fteid_t
dl_fteid
=
{};
sm_context_req_msg
.
get_dl_fteid
(
dl_fteid
);
// eNB's fteid
// we use the first dl_edge as we can only have one N3 interface
for
(
const
auto
&
qfi
:
list_of_qfis_to_be_modified
)
{
auto
flow
=
dl_edges
[
0
].
get_qos_flow
(
qfi
);
if
(
!
flow
)
{
// no QoS flow found
...
...
@@ -954,10 +1009,19 @@ smf_procedure_code session_update_sm_context_procedure::run(
}
else
{
flow
->
dl_fteid
=
dl_fteid
;
Logger
::
smf_app
().
debug
(
"Create FAR DL"
);
pfcp
::
create_far
create_far
=
pfcp_create_far
(
dl_edges
[
0
],
flow
->
qfi
);
synch_ul_dl_edges
(
dl_edges
,
ul_edges
,
flow
->
qfi
);
// Add IEs to message
n4_triggered
->
pfcp_ies
.
set
(
create_far
);
// for each UL edge we need a FAR, because of UL CL
edge
dl_edge
=
dl_edges
[
0
];
for
(
auto
&
edge
:
ul_edges
)
{
// we set PDR ID UL to 0, so we create new ones
auto
flow_dl
=
dl_edge
.
get_qos_flow
(
flow
->
qfi
);
flow_dl
->
pdr_id_dl
=
0
;
pfcp
::
create_far
create_far
=
pfcp_create_far
(
dl_edge
,
flow
->
qfi
);
synch_ul_dl_edges
(
dl_edges
,
ul_edges
,
flow
->
qfi
);
// Add IEs to message
n4_triggered
->
pfcp_ies
.
set
(
create_far
);
}
send_n4
=
true
;
Logger
::
smf_app
().
debug
(
...
...
@@ -971,9 +1035,13 @@ smf_procedure_code session_update_sm_context_procedure::run(
//-------------------
// IE create_pdr
//-------------------
pfcp
::
create_pdr
create_pdr
=
pfcp_create_pdr
(
ul_edges
[
0
],
flow
->
qfi
);
n4_triggered
->
pfcp_ies
.
set
(
create_pdr
);
synch_ul_dl_edges
(
dl_edges
,
ul_edges
,
flow
->
qfi
);
// for each UL edge we need a PDR
for
(
auto
&
ul_edge
:
ul_edges
)
{
pfcp
::
create_pdr
create_pdr
=
pfcp_create_pdr
(
ul_edge
,
flow
->
qfi
);
n4_triggered
->
pfcp_ies
.
set
(
create_pdr
);
synch_ul_dl_edges
(
dl_edges
,
ul_edges
,
flow
->
qfi
);
}
send_n4
=
true
;
Logger
::
smf_app
().
debug
(
...
...
@@ -1192,12 +1260,18 @@ smf_procedure_code session_update_sm_context_procedure::handle_itti_msg(
std
::
vector
<
pfcp
::
qfi_t
>
list_of_qfis_to_be_modified
=
{};
n11_trigger
->
req
.
get_qfis
(
list_of_qfis_to_be_modified
);
std
::
shared_ptr
<
upf_graph
>
graph
=
sps
->
get_sessions_graph
();
std
::
shared_ptr
<
pfcp_association
>
current_upf
;
std
::
vector
<
edge
>
dl_edges
;
std
::
vector
<
edge
>
ul_edges
;
std
::
shared_ptr
<
pfcp_association
>
current_upf
{};
std
::
vector
<
edge
>
dl_edges
{};
std
::
vector
<
edge
>
ul_edges
{};
graph
->
dfs_current_upf
(
dl_edges
,
ul_edges
,
current_upf
);
if
(
get_current_upf
(
dl_edges
,
ul_edges
,
current_upf
)
==
smf_procedure_code
::
ERROR
)
{
Logger
::
smf_app
().
error
(
"SMF DL procedure: Could not get current UPF"
);
// TODO is this enough as an error message? We have cause 31 but not values
return
smf_procedure_code
::
ERROR
;
}
bool
continue_n4
=
true
;
Logger
::
smf_app
().
debug
(
"Session procedure type: %s"
,
...
...
@@ -1218,13 +1292,13 @@ smf_procedure_code session_update_sm_context_procedure::handle_itti_msg(
PDU_SESSION_MODIFICATION_UE_INITIATED_STEP2:
case
session_management_procedures_type_e
:
:
HO_PATH_SWITCH_REQ
:
case
session_management_procedures_type_e
:
:
N2_HO_PREPARATION_PHASE_STEP2
:
{
pfcp
::
fteid_t
dl_fteid
=
{};
n11_trigger
->
req
.
get_dl_fteid
(
dl_fteid
);
pfcp
::
fteid_t
n3_
dl_fteid
=
{};
n11_trigger
->
req
.
get_dl_fteid
(
n3_
dl_fteid
);
Logger
::
smf_app
().
debug
(
"AN F-TEID ID"
"0x%"
PRIx32
", IP Addr %s"
,
dl_fteid
.
teid
,
conv
::
toString
(
dl_fteid
.
ipv4_address
).
c_str
());
n3_dl_fteid
.
teid
,
conv
::
toString
(
n3_
dl_fteid
.
ipv4_address
).
c_str
());
std
::
map
<
uint8_t
,
qos_flow_context_updated
>
qos_flow_context_to_be_updateds
=
{};
...
...
@@ -1239,36 +1313,42 @@ smf_procedure_code session_update_sm_context_procedure::handle_itti_msg(
for
(
const
auto
&
it_created_pdr
:
resp
.
pfcp_ies
.
created_pdrs
)
{
pfcp
::
pdr_id_t
pdr_id
=
{};
if
(
it_created_pdr
.
get
(
pdr_id
))
{
auto
flow
=
ul_edges
[
0
].
get_qos_flow
(
pdr_id
);
if
(
flow
)
{
Logger
::
smf_app
().
debug
(
"QoS Flow, QFI %d"
,
flow
->
qfi
.
qfi
);
for
(
const
auto
&
it
:
qos_flow_context_to_be_updateds
)
{
flow
->
dl_fteid
=
dl_fteid
;
if
(
it_created_pdr
.
get
(
flow
->
ul_fteid
))
{
Logger
::
smf_app
().
debug
(
"Got local_up_fteid from created_pdr %s"
,
flow
->
ul_fteid
.
toString
().
c_str
());
}
else
{
// UPF doesn't include its fteid in the response
Logger
::
smf_app
().
debug
(
"Could not get local_up_fteid from created_pdr"
);
}
for
(
auto
&
ul_edge
:
ul_edges
)
{
auto
flow
=
ul_edge
.
get_qos_flow
(
pdr_id
);
if
(
flow
)
{
Logger
::
smf_app
().
debug
(
"QoS Flow, QFI %d"
,
flow
->
qfi
.
qfi
);
for
(
const
auto
&
it
:
qos_flow_context_to_be_updateds
)
{
if
(
!
it_created_pdr
.
get
(
flow
->
dl_fteid
))
{
Logger
::
smf_app
().
warn
(
"Could not get DL FTEID from PDR in DL"
);
}
if
(
it_created_pdr
.
get
(
flow
->
ul_fteid
))
{
Logger
::
smf_app
().
debug
(
"Got local_up_fteid from created_pdr %s"
,
flow
->
ul_fteid
.
toString
().
c_str
());
}
else
{
// UPF doesn't include its fteid in the response
Logger
::
smf_app
().
debug
(
"Could not get local_up_fteid from created_pdr"
);
}
flow
->
released
=
false
;
// TODO can i safely remove that
// sps->add_qos_flow(flow);
qos_flow_context_updated
qcu
=
{};
qcu
.
set_cause
(
static_cast
<
uint8_t
>
(
cause_value_5gsm_e
::
CAUSE_255_REQUEST_ACCEPTED
));
qcu
.
set_qfi
(
pfcp
::
qfi_t
(
it
.
first
));
qcu
.
set_ul_fteid
(
flow
->
ul_fteid
);
qcu
.
set_dl_fteid
(
flow
->
dl_fteid
);
qcu
.
set_qos_profile
(
flow
->
qos_profile
);
n11_triggered_pending
->
res
.
add_qos_flow_context_updated
(
qcu
);
// TODO: remove this QFI from the list (as well as in
// n11_trigger->req)
break
;
flow
->
released
=
false
;
// TODO can i safely remove that
// sps->add_qos_flow(flow);
qos_flow_context_updated
qcu
=
{};
qcu
.
set_cause
(
static_cast
<
uint8_t
>
(
cause_value_5gsm_e
::
CAUSE_255_REQUEST_ACCEPTED
));
qcu
.
set_qfi
(
pfcp
::
qfi_t
(
it
.
first
));
qcu
.
set_ul_fteid
(
flow
->
ul_fteid
);
qcu
.
set_dl_fteid
(
flow
->
dl_fteid
);
qcu
.
set_qos_profile
(
flow
->
qos_profile
);
n11_triggered_pending
->
res
.
add_qos_flow_context_updated
(
qcu
);
// TODO: remove this QFI from the list (as well as in
// n11_trigger->req)
break
;
}
}
}
}
else
{
...
...
@@ -1281,32 +1361,39 @@ smf_procedure_code session_update_sm_context_procedure::handle_itti_msg(
if
(
cause
.
cause_value
==
CAUSE_VALUE_REQUEST_ACCEPTED
)
{
// TODO failed rule id
for
(
const
auto
&
it_update_far
:
n4_triggered
->
pfcp_ies
.
update_fars
)
{
// TODO Stefan: I think when there is an update FAR in DL, we dont
// need to update other UPFs, is that correct?
continue_n4
=
false
;
pfcp
::
far_id_t
far_id
=
{};
if
(
it_update_far
.
get
(
far_id
))
{
auto
flow
=
ul_edges
[
0
].
get_qos_flow
(
far_id
);
if
(
flow
)
{
for
(
const
auto
&
it
:
qos_flow_context_to_be_updateds
)
{
if
(
it
.
first
==
flow
->
qfi
.
qfi
)
{
flow
->
dl_fteid
=
dl_fteid
;
// TODO can i safely remove that?
// sps->add_qos_flow(flow);
qos_flow_context_updated
qcu
=
{};
qcu
.
set_cause
(
static_cast
<
uint8_t
>
(
cause_value_5gsm_e
::
CAUSE_255_REQUEST_ACCEPTED
));
qcu
.
set_qfi
(
pfcp
::
qfi_t
(
it
.
first
));
qcu
.
set_ul_fteid
(
flow
->
ul_fteid
);
qcu
.
set_dl_fteid
(
flow
->
dl_fteid
);
qcu
.
set_qos_profile
(
flow
->
qos_profile
);
n11_triggered_pending
->
res
.
add_qos_flow_context_updated
(
qcu
);
break
;
for
(
auto
&
ul_edge
:
ul_edges
)
{
auto
flow
=
ul_edge
.
get_qos_flow
(
far_id
);
if
(
flow
)
{
for
(
const
auto
&
it
:
qos_flow_context_to_be_updateds
)
{
if
(
it
.
first
==
flow
->
qfi
.
qfi
)
{
flow
->
dl_fteid
=
n3_dl_fteid
;
// TODO can i safely remove that?
// sps->add_qos_flow(flow);
qos_flow_context_updated
qcu
=
{};
qcu
.
set_cause
(
static_cast
<
uint8_t
>
(
cause_value_5gsm_e
::
CAUSE_255_REQUEST_ACCEPTED
));
qcu
.
set_qfi
(
pfcp
::
qfi_t
(
it
.
first
));
qcu
.
set_ul_fteid
(
flow
->
ul_fteid
);
qcu
.
set_dl_fteid
(
flow
->
dl_fteid
);
qcu
.
set_qos_profile
(
flow
->
qos_profile
);
n11_triggered_pending
->
res
.
add_qos_flow_context_updated
(
qcu
);
break
;
}
}
}
else
{
Logger
::
smf_app
().
error
(
"Could not get QoS flow for far_id for update_far in %s"
,
resp
.
pfcp_ies
.
get_msg_name
());
}
}
else
{
Logger
::
smf_app
().
error
(
"Could not get QoS flow for far_id for update_far in %s"
,
resp
.
pfcp_ies
.
get_msg_name
());
}
}
else
{
Logger
::
smf_app
().
error
(
...
...
@@ -1343,6 +1430,15 @@ smf_procedure_code session_update_sm_context_procedure::handle_itti_msg(
}
}
std
::
shared_ptr
<
pfcp_association
>
next_upf
{};
std
::
vector
<
edge
>
next_dl_edges
{};
std
::
vector
<
edge
>
next_ul_edges
{};
if
(
continue_n4
&&
get_next_upf
(
next_dl_edges
,
next_ul_edges
,
next_upf
)
==
smf_procedure_code
::
CONTINUE
)
{
return
send_n4_session_modification_request
();
}
n11_triggered_pending
->
res
.
set_cause
(
cause
.
cause_value
);
n11_triggered_pending
->
res
.
set_http_code
(
http_status_code_e
::
HTTP_STATUS_CODE_200_OK
);
...
...
src/smf_app/smf_procedure.hpp
View file @
35961b90
...
...
@@ -178,19 +178,19 @@ class session_create_sm_context_procedure : public smf_session_procedure {
itti_n4_session_establishment_response
&
resp
,
std
::
shared_ptr
<
smf
::
smf_context
>
sc
)
override
;
/**
* Sends a session establishment request, based on current UPF graph
* @return
*/
smf_procedure_code
send_n4_session_establishment_request
();
std
::
shared_ptr
<
itti_n4_session_establishment_request
>
n4_triggered
;
std
::
shared_ptr
<
itti_n11_create_sm_context_request
>
n11_trigger
;
std
::
shared_ptr
<
itti_n11_create_sm_context_response
>
n11_triggered_pending
;
private:
smf_qos_flow
current_flow
;
smf_qos_flow
current_flow
{};
/**
* Sends a session establishment request, based on current UPF graph
* @return
*/
smf_procedure_code
send_n4_session_establishment_request
();
};
//------------------------------------------------------------------------------
...
...
@@ -231,6 +231,16 @@ class session_update_sm_context_procedure : public smf_session_procedure {
std
::
shared_ptr
<
itti_n11_update_sm_context_request
>
n11_trigger
;
std
::
shared_ptr
<
itti_n11_update_sm_context_response
>
n11_triggered_pending
;
session_management_procedures_type_e
session_procedure_type
;
private:
// TODO currently support only one flow
smf_qos_flow
current_flow
{};
/**
* Sends a session modification request, based on the graph
* Does only consider normal DL procedures
* @return OK when successful, ERROR otherwise
*/
smf_procedure_code
send_n4_session_modification_request
();
};
//------------------------------------------------------------------------------
...
...
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