mirror of
https://gerrit.osmocom.org/erlang/osmo-epdg
synced 2024-08-13 06:26:24 +00:00
s2b: Implement GTPv2C DeleteBearerReq
Sessions are now stored/kept upon CreateSession time until deleted through DeleteBearerReq. Related: OS#6046 Change-Id: I1e5af1ead17385d2e494f4c90ffe6455aee850da
This commit is contained in:
parent
757cca01d8
commit
d6600aae20
1 changed files with 124 additions and 26 deletions
|
@ -70,7 +70,9 @@
|
||||||
rport :: non_neg_integer(),
|
rport :: non_neg_integer(),
|
||||||
restart_counter :: 0..255,
|
restart_counter :: 0..255,
|
||||||
seq_no :: 0..16#ffffffff,
|
seq_no :: 0..16#ffffffff,
|
||||||
sess_list %% TODO: fill it, list of gtp_session
|
next_local_control_tei = 1 :: 0..16#ffffffff,
|
||||||
|
next_local_data_tei = 1 :: 0..16#ffffffff,
|
||||||
|
sessions = sets:new()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
-record(gtp_bearer, {
|
-record(gtp_bearer, {
|
||||||
|
@ -134,36 +136,45 @@ create_session_req(Imsi) ->
|
||||||
gen_server:call(?SERVER,
|
gen_server:call(?SERVER,
|
||||||
{gtpc_create_session_req, {Imsi}}).
|
{gtpc_create_session_req, {Imsi}}).
|
||||||
|
|
||||||
handle_call({gtpc_create_session_req, {Imsi}}, _From, State) ->
|
handle_call({gtpc_create_session_req, {Imsi}}, _From, State0) ->
|
||||||
Sess = new_gtp_session(Imsi, State),
|
{Sess0, State1} = find_or_new_gtp_session(Imsi, State0),
|
||||||
Req = gen_create_session_request(Sess, State),
|
Req = gen_create_session_request(Sess0, State1),
|
||||||
%TODO: increment State.seq_no.
|
%TODO: increment State.seq_no.
|
||||||
tx_gtp(Req, State),
|
tx_gtp(Req, State1),
|
||||||
lager:debug("Waiting for CreateSessionResponse~n", []),
|
lager:debug("Waiting for CreateSessionResponse~n", []),
|
||||||
receive
|
receive
|
||||||
{udp, _Socket, IP, InPortNo, RxMsg} ->
|
{udp, _Socket, IP, InPortNo, RxMsg} ->
|
||||||
try
|
try
|
||||||
Resp = gtp_packet:decode(RxMsg),
|
Resp = gtp_packet:decode(RxMsg),
|
||||||
logger:info("s2b: Rx from IP ~p port ~n ~p~n", [IP, InPortNo, Resp]),
|
lager:info("s2b: Rx from IP ~p port ~p ~p~n", [IP, InPortNo, Resp]),
|
||||||
%% TODO: store Sess in State.
|
Sess1 = update_gtp_session_from_create_session_response(Resp, Sess0),
|
||||||
{reply, {ok, Resp}, State}
|
lager:info("s2b: Updated Session after create_session_response: ~p~n", [Sess1]),
|
||||||
|
State2 = update_gtp_session(Sess0, Sess1, State1),
|
||||||
|
{reply, {ok, Resp}, State2}
|
||||||
catch Any ->
|
catch Any ->
|
||||||
logger:error("Error sending message to receiver, ERROR: ~p~n", [Any]),
|
lager:error("Error sending message to receiver, ERROR: ~p~n", [Any]),
|
||||||
{reply, {error, decode_failure}, State}
|
{reply, {error, decode_failure}, State1}
|
||||||
end
|
end
|
||||||
after 5000 ->
|
after 5000 ->
|
||||||
logger:error("Timeout waiting for CreateSessionResponse for ~p~n", [Req]),
|
lager:error("Timeout waiting for CreateSessionResponse for ~p~n", [Req]),
|
||||||
{reply, timeout, State}
|
{reply, timeout, State1}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @callback gen_server
|
%% @callback gen_server
|
||||||
handle_cast(stop, State) ->
|
handle_cast(stop, State) ->
|
||||||
{stop, normal, State};
|
{stop, normal, State};
|
||||||
handle_cast(_Req, State) ->
|
handle_cast(Req, State) ->
|
||||||
|
lager:info("S2b handle_cast: ~p ~n", [Req]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%% @callback gen_server
|
%% @callback gen_server
|
||||||
handle_info(_Info, State) ->
|
handle_info({udp, _Socket, IP, InPortNo, RxMsg}, State) ->
|
||||||
|
lager:info("S2b: Rx from IP ~p port ~p: ~p~n", [IP, InPortNo, RxMsg]),
|
||||||
|
Req = gtp_packet:decode(RxMsg),
|
||||||
|
lager:info("S2b: Rx from IP ~p port ~p: ~p~n", [IP, InPortNo, Req]),
|
||||||
|
rx_gtp(Req, State);
|
||||||
|
handle_info(Info, State) ->
|
||||||
|
lager:info("S2b handle_info: ~p ~n", [Info]),
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%% @callback gen_server
|
%% @callback gen_server
|
||||||
|
@ -185,6 +196,76 @@ terminate(_Reason, _State) ->
|
||||||
%% Internal Function Definitions
|
%% Internal Function Definitions
|
||||||
%% ------------------------------------------------------------------
|
%% ------------------------------------------------------------------
|
||||||
|
|
||||||
|
new_gtp_session(Imsi, State) ->
|
||||||
|
% TODO: find non-used local TEI inside State
|
||||||
|
Bearer = #gtp_bearer{
|
||||||
|
ebi = 5,
|
||||||
|
local_data_tei = State#gtp_state.next_local_data_tei
|
||||||
|
},
|
||||||
|
Sess = #gtp_session{imsi = Imsi,
|
||||||
|
apn = ?APN,
|
||||||
|
local_control_tei = State#gtp_state.next_local_control_tei,
|
||||||
|
bearer = Bearer
|
||||||
|
},
|
||||||
|
NewSt = State#gtp_state{next_local_control_tei = State#gtp_state.next_local_control_tei + 1,
|
||||||
|
next_local_data_tei = State#gtp_state.next_local_data_tei + 1,
|
||||||
|
sessions = sets:add_element(Sess, State#gtp_state.sessions)},
|
||||||
|
{Sess, NewSt}.
|
||||||
|
|
||||||
|
% returns Sess if found, undefined it not
|
||||||
|
find_gtp_session_by_imsi(Imsi, State) ->
|
||||||
|
sets:fold(
|
||||||
|
fun(SessIt = #gtp_session{imsi = Imsi}, _AccIn) -> SessIt;
|
||||||
|
(_, AccIn) -> AccIn
|
||||||
|
end,
|
||||||
|
undefined,
|
||||||
|
State#gtp_state.sessions).
|
||||||
|
|
||||||
|
find_or_new_gtp_session(Imsi, State) ->
|
||||||
|
Sess = find_gtp_session_by_imsi(Imsi, State),
|
||||||
|
case Sess of
|
||||||
|
#gtp_session{imsi = Imsi} ->
|
||||||
|
{Sess, State};
|
||||||
|
undefined ->
|
||||||
|
new_gtp_session(Imsi, State)
|
||||||
|
end.
|
||||||
|
|
||||||
|
update_gtp_session(OldSess, NewSess, State) ->
|
||||||
|
SetRemoved = sets:del_element(OldSess, State#gtp_state.sessions),
|
||||||
|
SetUpdated = sets:add_element(NewSess, SetRemoved),
|
||||||
|
State#gtp_state{sessions = SetUpdated}.
|
||||||
|
|
||||||
|
delete_gtp_session(Sess, State) ->
|
||||||
|
SetRemoved = sets:del_element(Sess, State#gtp_state.sessions),
|
||||||
|
State#gtp_state{sessions = SetRemoved}.
|
||||||
|
|
||||||
|
update_gtp_session_from_create_session_response_ie(none, Sess) ->
|
||||||
|
Sess;
|
||||||
|
update_gtp_session_from_create_session_response_ie({_,
|
||||||
|
#v2_fully_qualified_tunnel_endpoint_identifier{
|
||||||
|
interface_type = _Interface,
|
||||||
|
key = TEI, ipv4 = _IP4, ipv6 = _IP6},
|
||||||
|
Next}, Sess) ->
|
||||||
|
update_gtp_session_from_create_session_response_ie(maps:next(Next), Sess#gtp_session{remote_control_tei = TEI});
|
||||||
|
update_gtp_session_from_create_session_response_ie({_, _, Next},
|
||||||
|
Sess) ->
|
||||||
|
update_gtp_session_from_create_session_response_ie(maps:next(Next), Sess).
|
||||||
|
|
||||||
|
update_gtp_session_from_create_session_response_ies(#gtp{ie = IEs}, Sess) ->
|
||||||
|
update_gtp_session_from_create_session_response_ie(maps:next(maps:iterator(IEs)), Sess).
|
||||||
|
|
||||||
|
update_gtp_session_from_create_session_response(Resp = #gtp{version = v2, type = create_session_response}, Sess) ->
|
||||||
|
update_gtp_session_from_create_session_response_ies(#gtp{ie = Resp#gtp.ie}, Sess).
|
||||||
|
|
||||||
|
% returns Sess if found, undefined it not
|
||||||
|
find_gtp_session_by_local_teic(LocalControlTei, State) ->
|
||||||
|
sets:fold(
|
||||||
|
fun(SessIt = #gtp_session{local_control_tei = LocalControlTei}, _AccIn) -> SessIt;
|
||||||
|
(_, AccIn) -> AccIn
|
||||||
|
end,
|
||||||
|
undefined,
|
||||||
|
State#gtp_state.sessions).
|
||||||
|
|
||||||
%% connect/2
|
%% connect/2
|
||||||
connect(Name, {Socket, RemoteAddr, RemotePort}) ->
|
connect(Name, {Socket, RemoteAddr, RemotePort}) ->
|
||||||
lager:info("~s connecting to IP ~s port ~p~n", [Name, RemoteAddr, RemotePort]),
|
lager:info("~s connecting to IP ~s port ~p~n", [Name, RemoteAddr, RemotePort]),
|
||||||
|
@ -193,23 +274,27 @@ connect(Name, {Socket, RemoteAddr, RemotePort}) ->
|
||||||
connect(Address) ->
|
connect(Address) ->
|
||||||
connect(?SVC_NAME, Address).
|
connect(?SVC_NAME, Address).
|
||||||
|
|
||||||
|
rx_gtp(Req = #gtp{version = v2, type = delete_bearer_request}, State) ->
|
||||||
|
Sess = find_gtp_session_by_local_teic(Req#gtp.tei, State),
|
||||||
|
case Sess of
|
||||||
|
undefined ->
|
||||||
|
lager:error("Rx unknown TEI ~p: ~p~n", [Req#gtp.tei, Req]),
|
||||||
|
{noreply, State};
|
||||||
|
Sess ->
|
||||||
|
Resp = gen_delete_bearer_response(Req, Sess, request_accepted, State),
|
||||||
|
tx_gtp(Resp, State),
|
||||||
|
State1 = delete_gtp_session(Sess, State),
|
||||||
|
{noreply, State1}
|
||||||
|
end;
|
||||||
|
rx_gtp(Req, State) ->
|
||||||
|
lager:error("S2b: UNIMPLEMENTED Rx: ~p~n", [Req]),
|
||||||
|
{noreply, State}.
|
||||||
|
|
||||||
tx_gtp(Req, State) ->
|
tx_gtp(Req, State) ->
|
||||||
lager:info("s2b: Tx ~p~n", [Req]),
|
lager:info("s2b: Tx ~p~n", [Req]),
|
||||||
Msg = gtp_packet:encode(Req),
|
Msg = gtp_packet:encode(Req),
|
||||||
gen_udp:send(State#gtp_state.socket, State#gtp_state.raddr, State#gtp_state.rport, Msg).
|
gen_udp:send(State#gtp_state.socket, State#gtp_state.raddr, State#gtp_state.rport, Msg).
|
||||||
|
|
||||||
new_gtp_session(Imsi, _State) ->
|
|
||||||
% TODO: find non-used local TEI inside State
|
|
||||||
Bearer = #gtp_bearer{
|
|
||||||
ebi = 5,
|
|
||||||
local_data_tei = 1
|
|
||||||
},
|
|
||||||
#gtp_session{imsi = Imsi,
|
|
||||||
apn = ?APN,
|
|
||||||
local_control_tei = 0,
|
|
||||||
bearer = Bearer
|
|
||||||
}.
|
|
||||||
|
|
||||||
%% 7.2.1 Create Session Request
|
%% 7.2.1 Create Session Request
|
||||||
gen_create_session_request(#gtp_session{imsi = Imsi,
|
gen_create_session_request(#gtp_session{imsi = Imsi,
|
||||||
apn = Apn,
|
apn = Apn,
|
||||||
|
@ -249,4 +334,17 @@ gen_create_session_request(#gtp_session{imsi = Imsi,
|
||||||
],
|
],
|
||||||
#gtp{version = v2, type = create_session_request, tei = 0, seq_no = SeqNo, ie = IEs}.
|
#gtp{version = v2, type = create_session_request, tei = 0, seq_no = SeqNo, ie = IEs}.
|
||||||
|
|
||||||
|
gen_delete_bearer_response(Req = #gtp{version = v2, type = delete_bearer_request},
|
||||||
|
Sess = #gtp_session{remote_control_tei = RemoteCtlTEI},
|
||||||
|
GtpCause,
|
||||||
|
#gtp_state{restart_counter = RCnt}) ->
|
||||||
|
IEs = [#v2_recovery{restart_counter = RCnt},
|
||||||
|
#v2_cause{v2_cause = GtpCause}
|
||||||
|
],
|
||||||
|
#gtp{version = v2,
|
||||||
|
type = delete_bearer_response,
|
||||||
|
tei = RemoteCtlTEI,
|
||||||
|
seq_no = Req#gtp.seq_no,
|
||||||
|
ie = IEs}.
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue