Skip to content

Commit

Permalink
properly handle Change Reporting Indication/Action
Browse files Browse the repository at this point in the history
Semantics are slightly different on GTP v1 and v2, but close enough.
  • Loading branch information
RoadRunnr committed Sep 28, 2020
1 parent ca16498 commit ee78d74
Show file tree
Hide file tree
Showing 11 changed files with 252 additions and 17 deletions.
2 changes: 1 addition & 1 deletion rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
{git,"https://github.com/uwiger/setup.git",
{ref,"b8fd827e7e9e0f5aeec2f26a2f3a31bb06407686"}},
0},
{<<"stacktrace_compat">>,{pkg,<<"stacktrace_compat">>,<<"1.0.2">>},2}]}.
{<<"stacktrace_compat">>,{pkg,<<"stacktrace_compat">>,<<"1.0.2">>},1}]}.
[
{pkg_hash,[
{<<"accept">>, <<"B33B127ABCA7CC948BBE6CAA4C263369ABF1347CFA9D8E699C6D214660F10CD1">>},
Expand Down
8 changes: 8 additions & 0 deletions src/ergw_charging.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
-module(ergw_charging).

-export([validate_options/1,
reporting_triggers/0,
is_charging_event/2,
is_enabled/1,
rulebase/0]).
Expand Down Expand Up @@ -163,6 +164,13 @@ config() ->
_ -> #{}
end.

reporting_triggers() ->
Triggers =
maps:get(triggers,
maps:get(offline, config(), #{}), #{}),
maps:map(
fun(_Key, Cond) -> Cond /= 'off' end, Triggers).

is_charging_event(offline, Evs) ->
Filter =
maps:get(triggers,
Expand Down
22 changes: 19 additions & 3 deletions src/ggsn_gn.erl
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ handle_request(ReqKey,
ok
end,

ResponseIEs = create_pdp_context_response(Result, ActiveSessionOpts, IEs, Context),
ResponseIEs = create_pdp_context_response(Result, ActiveSessionOpts, Request, Context),
Response = response(create_pdp_context_response, Context, ResponseIEs, Request),
gtp_context:send_response(ReqKey, Request, Response),

Expand Down Expand Up @@ -1177,15 +1177,31 @@ tunnel_endpoint_elements(#context{control_port = #gtp_port{ip = CntlIP},
context_charging_id(#context{charging_identifier = ChargingId}) ->
#charging_id{id = <<ChargingId:32>>}.

create_pdp_context_response(Result, SessionOpts, RequestIEs,
change_reporting_action(true, #{'user-location-info-change' := true}, IE) ->
[#ms_info_change_reporting_action{action = start_reporting_cgi_sai}|IE];
change_reporting_action(true, #{'cgi-sai-change' := true}, IE) ->
[#ms_info_change_reporting_action{action = start_reporting_cgi_sai}|IE];
change_reporting_action(true, #{'rai-change' := true}, IE) ->
[#ms_info_change_reporting_action{action = start_reporting_rai}|IE];
change_reporting_action(_, _Triggers, IE) ->
IE.

change_reporting_actions(#gtp{ext_hdr = ExtHdr}, IEs) ->
Triggers = ergw_charging:reporting_triggers(),

CRSI = proplists:get_bool(ms_info_change_reporting_support_indication, ExtHdr),
_IE = change_reporting_action(CRSI, Triggers, IEs).

create_pdp_context_response(Result, SessionOpts, #gtp{ie = RequestIEs} = Request,
#context{ms_v4 = MSv4, ms_v6 = MSv6} = Context) ->
IE0 = [Result,
#reordering_required{required = no},
context_charging_id(Context),
encode_eua(MSv4, MSv6)],
IE1 = pdp_qos_profile(SessionOpts, IE0),
IE2 = pdp_pco(SessionOpts, RequestIEs, IE1),
tunnel_endpoint_elements(Context, IE2).
IE3 = tunnel_endpoint_elements(Context, IE2),
_IE = change_reporting_actions(Request, IE3).

%% Wrapper for gen_statem state_callback_result Actions argument
%% Timeout set in the context of a prolonged idle gtp session
Expand Down
57 changes: 55 additions & 2 deletions src/pgw_s5s8.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1308,18 +1308,71 @@ s5s8_pgw_gtp_c_tei(#context{control_port = #gtp_port{ip = IP}, local_control_tei
s5s8_pgw_gtp_u_tei(#context{local_data_endp = #gtp_endp{ip = IP, teid = TEI}}) ->
fq_teid(2, ?'S5/S8-U PGW', TEI, IP).

cr_ran_type(1) -> 'UTRAN';
cr_ran_type(2) -> 'UTRAN';
cr_ran_type(6) -> 'EUTRAN';
cr_ran_type(8) -> 'EUTRAN';
cr_ran_type(9) -> 'EUTRAN';
cr_ran_type(10) -> 'NR';
cr_ran_type(_) -> undefined.

%% it is unclear from TS 29.274 if the CRA IE can only be included when the
%% SGSN/MME has indicated support for it in the Indication IE.
%% Some comments in Modify Bearer Request suggest that it might be possbile
%% to unconditionally set it, other places state that is should only be sent
%% when the SGSN/MME indicated support for it.
%% For the moment only include it when the CRSI flag was set.

change_reporting_action(true, ENBCRSI, #{?'RAT Type' :=
#v2_rat_type{rat_type = Type}}, Trigger, IE) ->
change_reporting_action(ENBCRSI, cr_ran_type(Type), Trigger, IE);
change_reporting_action(_, _, _, _, IE) ->
IE.

change_reporting_action(true, 'EUTRAN', #{'tai-change' := true,
'user-location-info-change' := true}, IE) ->
[#v2_change_reporting_action{
action = start_reporting_tai__macro_enodeb_id_and_extended_macro_enodeb_id}|IE];
change_reporting_action(true, 'EUTRAN', #{'user-location-info-change' := true}, IE) ->
[#v2_change_reporting_action{
action = start_reporting_macro_enodeb_id_and_extended_macro_enodeb_id}|IE];
change_reporting_action(_, 'EUTRAN', #{'tai-change' := true, 'ecgi-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_tai_and_ecgi}|IE];
change_reporting_action(_, 'EUTRAN', #{'tai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_tai}|IE];
change_reporting_action(_, 'EUTRAN', #{'ecgi-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_ecgi}|IE];
change_reporting_action(_, 'UTRAN', #{'user-location-info-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_cgi_sai_and_rai}|IE];
change_reporting_action(_, 'UTRAN', #{'cgi-sai-change' := true, 'rai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_cgi_sai_and_rai}|IE];
change_reporting_action(_, 'UTRAN', #{'cgi-sai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_cgi_sai}|IE];
change_reporting_action(_, 'UTRAN', #{'rai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_rai}|IE];
change_reporting_action(_, _, _Triggers, IE) ->
IE.

change_reporting_actions(RequestIEs, IE0) ->
Indications = gtp_v2_c:get_indication_flags(RequestIEs),
Triggers = ergw_charging:reporting_triggers(),

CRSI = proplists:get_bool('CRSI', Indications),
ENBCRSI = proplists:get_bool('ENBCRSI', Indications),
_IE = change_reporting_action(CRSI, ENBCRSI, RequestIEs, Triggers, IE0).

create_session_response(Result, SessionOpts, RequestIEs, EBI,
#context{ms_v4 = MSv4, ms_v6 = MSv6} = Context) ->

IE0 = bearer_context(EBI, Context, []),
IE1 = pdn_pco(SessionOpts, RequestIEs, IE0),
IE2 = change_reporting_actions(RequestIEs, IE1),

[Result,
#v2_change_reporting_action{action = start_reporting_tai_and_ecgi},
#v2_apn_restriction{restriction_type_value = 0},
context_charging_id(Context),
s5s8_pgw_gtp_c_tei(Context),
encode_paa(MSv4, MSv6) | IE1].
encode_paa(MSv4, MSv6) | IE2].

%% Wrapper for gen_statem state_callback_result Actions argument
%% Timeout set in the context of a prolonged idle gtpv2 session
Expand Down
57 changes: 55 additions & 2 deletions src/saegw_s11.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1223,19 +1223,72 @@ s5s8_pgw_gtp_u_tei(#context{local_data_endp = #gtp_endp{ip = IP, teid = TEI}}) -
%% S5/S8 F-TEI Instance
fq_teid(2, ?'S5/S8-U PGW', TEI, IP).

cr_ran_type(1) -> 'UTRAN';
cr_ran_type(2) -> 'UTRAN';
cr_ran_type(6) -> 'EUTRAN';
cr_ran_type(8) -> 'EUTRAN';
cr_ran_type(9) -> 'EUTRAN';
cr_ran_type(10) -> 'NR';
cr_ran_type(_) -> undefined.

%% it is unclear from TS 29.274 if the CRA IE can only be included when the
%% SGSN/MME has indicated support for it in the Indication IE.
%% Some comments in Modify Bearer Request suggest that it might be possbile
%% to unconditionally set it, other places state that is should only be sent
%% when the SGSN/MME indicated support for it.
%% For the moment only include it when the CRSI flag was set.

change_reporting_action(true, ENBCRSI, #{?'RAT Type' :=
#v2_rat_type{rat_type = Type}}, Trigger, IE) ->
change_reporting_action(ENBCRSI, cr_ran_type(Type), Trigger, IE);
change_reporting_action(_, _, _, _, IE) ->
IE.

change_reporting_action(true, 'EUTRAN', #{'tai-change' := true,
'user-location-info-change' := true}, IE) ->
[#v2_change_reporting_action{
action = start_reporting_tai__macro_enodeb_id_and_extended_macro_enodeb_id}|IE];
change_reporting_action(true, 'EUTRAN', #{'user-location-info-change' := true}, IE) ->
[#v2_change_reporting_action{
action = start_reporting_macro_enodeb_id_and_extended_macro_enodeb_id}|IE];
change_reporting_action(_, 'EUTRAN', #{'tai-change' := true, 'ecgi-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_tai_and_ecgi}|IE];
change_reporting_action(_, 'EUTRAN', #{'tai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_tai}|IE];
change_reporting_action(_, 'EUTRAN', #{'ecgi-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_ecgi}|IE];
change_reporting_action(_, 'UTRAN', #{'user-location-info-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_cgi_sai_and_rai}|IE];
change_reporting_action(_, 'UTRAN', #{'cgi-sai-change' := true, 'rai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_cgi_sai_and_rai}|IE];
change_reporting_action(_, 'UTRAN', #{'cgi-sai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_cgi_sai}|IE];
change_reporting_action(_, 'UTRAN', #{'rai-change' := true}, IE) ->
[#v2_change_reporting_action{action = start_reporting_rai}|IE];
change_reporting_action(_, _, _Triggers, IE) ->
IE.

change_reporting_actions(RequestIEs, IE0) ->
Indications = gtp_v2_c:get_indication_flags(RequestIEs),
Triggers = ergw_charging:reporting_triggers(),

CRSI = proplists:get_bool('CRSI', Indications),
ENBCRSI = proplists:get_bool('ENBCRSI', Indications),
_IE = change_reporting_action(CRSI, ENBCRSI, RequestIEs, Triggers, IE0).

create_session_response(Result, SessionOpts, RequestIEs, EBI,
#context{ms_v4 = MSv4, ms_v6 = MSv6} = Context) ->

IE0 = bearer_context(EBI, Context, []),
IE1 = pdn_pco(SessionOpts, RequestIEs, IE0),
IE2 = change_reporting_actions(RequestIEs, IE1),

[Result,
#v2_change_reporting_action{action = start_reporting_tai_and_ecgi},
%% Sender F-TEID for Control Plane
s11_sender_f_teid(Context),
s5s8_pgw_gtp_c_tei(Context),
#v2_apn_restriction{restriction_type_value = 0},
encode_paa(MSv4, MSv6) | IE1].
encode_paa(MSv4, MSv6) | IE2].

%% Wrapper for gen_statem state_callback_result Actions argument
%% Timeout set in the context of a prolonged idle gtpv2 session
Expand Down
16 changes: 14 additions & 2 deletions test/ergw_ggsn_test_lib.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ delete_pdp_context(SubType, GtpC) ->
%%% Create GTPv1-C messages
%%%===================================================================

make_ms_info_change_reporting_indication(crsi, #gtp{ext_hdr = Hdr} = Req) ->
Req#gtp{ext_hdr = [ms_info_change_reporting_support_indication|Hdr]};
make_ms_info_change_reporting_indication(_SubType, Req) ->
Req.

validate_ms_info_change_reporting(crsi, IEs) ->
?equal(true, maps:is_key({ms_info_change_reporting_action,0}, IEs));
validate_ms_info_change_reporting(_SubType, IEs) ->
?equal(false, maps:is_key({ms_info_change_reporting_action,0}, IEs)).

make_pdp_type({Type, DABF, _}, IEs) ->
[#common_flags{flags = ['Dual Address Bearer Flag']} || DABF] ++
make_pdp_addr_cfg(Type, IEs);
Expand Down Expand Up @@ -256,8 +266,9 @@ make_request(create_pdp_context_request, SubType,
rac = 0}],
IEs = make_pdp_type(SubType, IEs0),

#gtp{version = v1, type = create_pdp_context_request, tei = 0,
seq_no = SeqNo, ie = IEs};
Req = #gtp{version = v1, type = create_pdp_context_request, tei = 0,
seq_no = SeqNo, ie = IEs},
make_ms_info_change_reporting_indication(SubType, Req);

make_request(update_pdp_context_request, SubType,
#gtpc{restart_counter = RCnt, seq_no = SeqNo,
Expand Down Expand Up @@ -505,6 +516,7 @@ validate_response(create_pdp_context_request, SubType, Response,
#tunnel_endpoint_identifier_data_i{}
}}, Response),
validate_pdp_type(SubType, Response#gtp.ie),
validate_ms_info_change_reporting(SubType, Response#gtp.ie),
GtpC = update_ue_ip(Response#gtp.ie, GtpC0),

#gtp{ie = #{{tunnel_endpoint_identifier_control_plane,0} :=
Expand Down
32 changes: 29 additions & 3 deletions test/ergw_pgw_test_lib.erl
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,31 @@ paa({{_,_,_,_,_,_,_,_} = IP, PrefixLen}) ->
address = <<PrefixLen:8, (ergw_inet:ip2bin(IP))/binary>>
}.

set_indication_flag(Flag, IEs) ->
Fs =
case lists:keyfind(v2_indication, 1, IEs) of
#v2_indication{flags = Flags} ->
Flags;
_ ->
[]
end,
lists:keystore(v2_indication, 1, IEs, #v2_indication{flags = [Flag|Fs]}).

make_indication(crsi, IEs) ->
set_indication_flag('CRSI', IEs);
make_indication(_SubType, IEs) ->
IEs.

validate_indication(crsi, IEs) ->
?equal(true, maps:is_key({v2_change_reporting_action,0}, IEs));
validate_indication(_SubType, IEs) ->
?equal(false, maps:is_key({v2_change_reporting_action,0}, IEs)).

make_pdn_type({Type, DABF, _}, IEs) ->
[#v2_indication{flags = ['DAF']} || DABF] ++
make_pdn_addr_cfg(Type, IEs);
case DABF of
true -> make_pdn_addr_cfg(Type, set_indication_flag('DAF', IEs));
false -> make_pdn_addr_cfg(Type, IEs)
end;
make_pdn_type(Type, IEs)
when Type =:= ipv4; Type =:= static_ipv4;
Type =:= ipv6; Type =:= static_ipv6;
Expand Down Expand Up @@ -386,6 +408,7 @@ make_request(create_session_request, SubType,
fq_teid(2, ?'S5/S8-U SGW', LocalDataTEI, LocalIP)
]},
fq_teid(0, ?'S5/S8-C SGW', LocalCntlTEI, LocalIP),
#v2_indication{flags = []},
#v2_international_mobile_subscriber_identity{
imsi = imsi(SubType, LocalCntlTEI)},
#v2_mobile_equipment_identity{mei = imei(SubType, LocalCntlTEI)},
Expand All @@ -396,7 +419,8 @@ make_request(create_session_request, SubType,
#v2_ue_time_zone{timezone = 10, dst = 0},
#v2_user_location_information{tai = <<3,2,22,214,217>>,
ecgi = <<3,2,22,8,71,9,92>>}],
IEs = make_pdn_type(SubType, IEs0),
IEs1 = make_pdn_type(SubType, IEs0),
IEs = make_indication(SubType, IEs1),
#gtp{version = v2, type = create_session_request, tei = 0,
seq_no = SeqNo, ie = IEs};

Expand Down Expand Up @@ -867,6 +891,7 @@ validate_response(create_session_request, SubType, Response,
interface_type = ?'S5/S8-U PGW'}}}
}}, Response),
validate_pdn_type(SubType, Response#gtp.ie),
validate_indication(SubType, Response#gtp.ie),
GtpC = update_ue_ip(Response#gtp.ie, GtpC0),

#gtp{ie = #{{v2_fully_qualified_tunnel_endpoint_identifier,1} :=
Expand Down Expand Up @@ -941,6 +966,7 @@ validate_response(modify_bearer_request, SubType, Response,
}, Response),
#gtp{ie = IEs} = Response,
?equal(false, maps:is_key({v2_bearer_context,0}, IEs)),
?equal(false, maps:is_key({v2_change_reporting_action,0}, IEs)),
GtpC;

validate_response(modify_bearer_command, _SubType, Response,
Expand Down

0 comments on commit ee78d74

Please sign in to comment.