Skip to content

Commit

Permalink
rework AAA session termination on PDP context closure
Browse files Browse the repository at this point in the history
Explicitly stop the AAA session whenever a PDP context is terminated.

Leaving the accounting stop handling to the AAA session creates a race
between collecting the accounting data from the data path context and
the removal of that context by the PDP delete function.

This is avoided by collecting the accounting data before the delete.
  • Loading branch information
Andreas Schultz committed Sep 7, 2017
1 parent 1d42db7 commit 2154986
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 45 deletions.
48 changes: 26 additions & 22 deletions src/ggsn_gn.erl
Expand Up @@ -85,7 +85,7 @@ init(_Opts, State) ->
{ok, State#{'Session' => Session}}.

handle_call(get_accounting, _From, #{context := Context} = State) ->
Counter = gtp_dp:get_accounting(Context),
Counter = get_accounting(Context),
{reply, Counter, State};

handle_call(delete_context, From, #{context := Context} = State) ->
Expand All @@ -98,17 +98,14 @@ handle_call(terminate_context, _From, #{context := Context} = State) ->
{stop, normal, ok, State};

handle_call({path_restart, Path}, _From,
#{context := #context{path = Path} = Context} = State) ->
dp_delete_pdp_context(Context),
pdp_release_ip(Context),
#{context := #context{path = Path}} = State) ->
close_pdp_context(State),
{stop, normal, ok, State};
handle_call({path_restart, _Path}, _From, State) ->
{reply, ok, State}.

handle_cast({packet_in, _GtpPort, _IP, _Port, #gtp{type = error_indication}},
#{context := Context} = State) ->
dp_delete_pdp_context(Context),
pdp_release_ip(Context),
handle_cast({packet_in, _GtpPort, _IP, _Port, #gtp{type = error_indication}}, State) ->
close_pdp_context(State),
{stop, normal, State};

handle_cast({packet_in, _GtpPort, _IP, _Port, _Msg}, State) ->
Expand Down Expand Up @@ -199,21 +196,16 @@ handle_request(_ReqKey,
handle_request(_ReqKey,
#gtp{type = delete_pdp_context_request, ie = _IEs}, _Resent,
#{context := Context} = State) ->

dp_delete_pdp_context(Context),
pdp_release_ip(Context),

close_pdp_context(State),
Reply = response(delete_pdp_context_response, Context, request_accepted),
{stop, Reply, State};

handle_request(ReqKey, _Msg, _Resent, State) ->
gtp_context:request_finished(ReqKey),
{noreply, State}.

handle_response(From, timeout, #gtp{type = delete_pdp_context_request},
#{context := Context} = State) ->
dp_delete_pdp_context(Context),
pdp_release_ip(Context),
handle_response(From, timeout, #gtp{type = delete_pdp_context_request}, State) ->
close_pdp_context(State),
gen_server:reply(From, {error, timeout}),
{stop, State};

Expand All @@ -223,8 +215,7 @@ handle_response(From,
_Request,
#{context := Context0} = State) ->
Context = gtp_path:bind(Response, Context0),
dp_delete_pdp_context(Context),
pdp_release_ip(Context),
close_pdp_context(State),
gen_server:reply(From, {ok, Cause}),
{stop, State#{context := Context}}.

Expand Down Expand Up @@ -260,20 +251,25 @@ authenticate(Context, Session, SessionOpts, Request) ->
context = Context})
end.

accounting_update(GTP, SessionOpts) ->
case gen_server:call(GTP, get_accounting) of
get_accounting(Context) ->
case gtp_dp:get_accounting(Context) of
{ok, #counter{rx = {RcvdBytes, RcvdPkts},
tx = {SendBytes, SendPkts}}} ->
Acc = [{'InPackets', RcvdPkts},
{'OutPackets', SendPkts},
{'InOctets', RcvdBytes},
{'OutOctets', SendBytes}],
ergw_aaa_session:merge(SessionOpts, to_session(Acc));
to_session(Acc);
_Other ->
lager:warning("got unexpected accounting: ~p", [_Other]),
SessionOpts
to_session([])
end.

accounting_update(GTP, SessionOpts) ->
lager:debug("accounting_update(~p, ~p)", [GTP, SessionOpts]),
Counter = gen_server:call(GTP, get_accounting),
ergw_aaa_session:merge(SessionOpts, Counter).

pdp_alloc(#end_user_address{pdp_type_organization = 1,
pdp_type_number = 16#21,
pdp_address = Address}) ->
Expand Down Expand Up @@ -327,6 +323,14 @@ encode_eua(Org, Number, IPv4, IPv6) ->
pdp_release_ip(#context{vrf = VRF, ms_v4 = MSv4, ms_v6 = MSv6}) ->
vrf:release_pdp_ip(VRF, MSv4, MSv6).

close_pdp_context(#{context := Context, 'Session' := Session} = State) ->
SessionOpts = get_accounting(Context),
lager:debug("Accounting Opts: ~p", [SessionOpts]),
ergw_aaa_session:stop(Session, SessionOpts),

dp_delete_pdp_context(Context),
pdp_release_ip(Context).

apply_context_change(NewContext0, OldContext, State) ->
NewContextPending = gtp_path:bind(NewContext0),
NewContext = dp_update_pdp_context(NewContextPending, OldContext),
Expand Down
51 changes: 28 additions & 23 deletions src/pgw_s5s8.erl
Expand Up @@ -94,30 +94,26 @@ init(_Opts, State) ->
{ok, State#{'Session' => Session}}.

handle_call(get_accounting, _From, #{context := Context} = State) ->
Counter = gtp_dp:get_accounting(Context),
Counter = get_accounting(Context),
{reply, Counter, State};

handle_call(delete_context, From, #{context := Context} = State) ->
delete_context(From, Context),
{noreply, State};

handle_call(terminate_context, _From, #{context := Context} = State) ->
dp_delete_pdp_context(Context),
pdn_release_ip(Context),
handle_call(terminate_context, _From, State) ->
close_pdn_context(State),
{stop, normal, ok, State};

handle_call({path_restart, Path}, _From,
#{context := #context{path = Path} = Context} = State) ->
dp_delete_pdp_context(Context),
pdn_release_ip(Context),
#{context := #context{path = Path}} = State) ->
close_pdn_context(State),
{stop, normal, ok, State};
handle_call({path_restart, _Path}, _From, State) ->
{reply, ok, State}.

handle_cast({packet_in, _GtpPort, _IP, _Port, #gtp{type = error_indication}},
#{context := Context} = State) ->
dp_delete_pdp_context(Context),
pdn_release_ip(Context),
handle_cast({packet_in, _GtpPort, _IP, _Port, #gtp{type = error_indication}}, State) ->
close_pdn_context(State),
{stop, normal, State};

handle_cast({packet_in, _GtpPort, _IP, _Port, _Msg}, State) ->
Expand Down Expand Up @@ -323,8 +319,7 @@ handle_request(_ReqKey,

case Result of
{ok, {ReplyIEs, State}} ->
dp_delete_pdp_context(Context),
pdn_release_ip(Context),
close_pdn_context(State),
Reply = response(delete_session_response, Context, ReplyIEs),
{stop, Reply, State};

Expand Down Expand Up @@ -365,10 +360,8 @@ handle_response(_, timeout, #gtp{type = update_bearer_request},
delete_context(undefined, Context),
{noreply, State};

handle_response(From, timeout, #gtp{type = delete_bearer_request},
#{context := Context} = State) ->
dp_delete_pdp_context(Context),
pdn_release_ip(Context),
handle_response(From, timeout, #gtp{type = delete_bearer_request}, State) ->
close_pdn_context(State),
if is_tuple(From) -> gen_server:reply(From, {error, timeout});
true -> ok
end,
Expand All @@ -380,8 +373,7 @@ handle_response(From,
_Request,
#{context := Context0} = State) ->
Context = gtp_path:bind(Response, Context0),
dp_delete_pdp_context(Context),
pdn_release_ip(Context),
close_pdn_context(State),
if is_tuple(From) -> gen_server:reply(From, {ok, Cause});
true -> ok
end,
Expand Down Expand Up @@ -421,20 +413,25 @@ authenticate(Context, Session, SessionOpts, Request) ->
context = Context})
end.

accounting_update(GTP, SessionOpts) ->
case gen_server:call(GTP, get_accounting) of
get_accounting(Context) ->
case gtp_dp:get_accounting(Context) of
{ok, #counter{rx = {RcvdBytes, RcvdPkts},
tx = {SendBytes, SendPkts}}} ->
Acc = [{'InPackets', RcvdPkts},
{'OutPackets', SendPkts},
{'InOctets', RcvdBytes},
{'OutOctets', SendBytes}],
ergw_aaa_session:merge(SessionOpts, to_session(Acc));
to_session(Acc);
_Other ->
lager:warning("got unexpected accounting: ~p", [_Other]),
SessionOpts
to_session([])
end.

accounting_update(GTP, SessionOpts) ->
lager:debug("accounting_update(~p, ~p)", [GTP, SessionOpts]),
Counter = gen_server:call(GTP, get_accounting),
ergw_aaa_session:merge(SessionOpts, Counter).

match_context(_Type, _Context, undefined) ->
error_m:return(ok);
match_context(Type,
Expand Down Expand Up @@ -482,6 +479,14 @@ encode_paa(Type, IPv4, IPv6) ->
pdn_release_ip(#context{vrf = VRF, ms_v4 = MSv4, ms_v6 = MSv6}) ->
vrf:release_pdp_ip(VRF, MSv4, MSv6).

close_pdn_context(#{context := Context, 'Session' := Session}) ->
SessionOpts = get_accounting(Context),
lager:debug("Accounting Opts: ~p", [SessionOpts]),
ergw_aaa_session:stop(Session, SessionOpts),

dp_delete_pdp_context(Context),
pdn_release_ip(Context).

apply_context_change(NewContext0, OldContext, State) ->
NewContextPending = gtp_path:bind(NewContext0),
NewContext = dp_update_pdp_context(NewContextPending, OldContext),
Expand Down

0 comments on commit 2154986

Please sign in to comment.