fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41624?usp=email )
Change subject: enb_proxy: add inital MME pooling support ......................................................................
enb_proxy: add inital MME pooling support
Change-Id: I83dc4a78c78a7b87e87f5ca9a941a168d6c1dc36 Related: SYS#7052 --- M src/enb_proxy.erl M src/s1ap_utils.erl 2 files changed, 74 insertions(+), 32 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/24/41624/1
diff --git a/src/enb_proxy.erl b/src/enb_proxy.erl index 7a51221..bc36c96 100644 --- a/src/enb_proxy.erl +++ b/src/enb_proxy.erl @@ -71,6 +71,8 @@ enb_conn_info :: sctp_server:conn_info(), mme_conn_cfg :: sctp_client:cfg(), s1setup_req :: undefined | binary(), + mme_name :: undefined | mme_registry:mme_name(), + enb_tacs :: undefined | [s1ap_utils:tac()], sock :: undefined | gen_sctp:sctp_socket(), enb_handle :: enb_registry:enb_handle(), genb_id_str :: undefined | string(), @@ -155,9 +157,13 @@ s1ap_proxy:set_genb_id(S#state.handler, GlobalENBId), enb_registry:enb_event(S#state.enb_handle, {s1setup, GENBId}), gtpu_kpi_enb_register(S#state{genb_id_str = GlobalENBId}), + %% fetch the TAC (Tracking Area Code) list + TACs = proplists:get_value(?'id-SupportedTAs', IEs), + ?LOG_DEBUG("Broadcast TACs: ~p", [TACs]), {next_state, connecting, S#state{s1setup_req = Data, - genb_id_str = GlobalENBId}}; + genb_id_str = GlobalENBId, + enb_tacs = TACs}}; {{Proc, Type}, IEs} -> ?LOG_ERROR("Rx unexpected S1AP PDU from eNB: ~p/~p, ~p", [Proc, Type, IEs]), {stop, {shutdown, s1setup_error}}; @@ -171,17 +177,40 @@
%% CONNECTING state connecting(enter, OldState, - #state{mme_conn_cfg = MmeConnCfg} = S) -> + #state{mme_name = MmeName, + enb_tacs = EnbTACs} = S) -> ?LOG_INFO("State change: ~p -> ~p", [OldState, ?FUNCTION_NAME]), - %% Initiate connection establishment with the MME - {ok, Sock} = sctp_client:connect(MmeConnCfg), - %% loop transition to enable state_timeout - {next_state, ?FUNCTION_NAME, S#state{sock = Sock}, - [{state_timeout, 2_000, conn_est_timeout}]}; + %% Select an MME from the pool + case mme_registry:mme_select(#{prev_mme => MmeName, + enb_tacs => EnbTACs}) of + {ok, MmeInfo} -> + ?LOG_INFO("MME selection: trying ~p", [maps:get(name, MmeInfo)]), + %% Close the old connection, if any + close_sock(S), + %% Initiate connection establishment with the MME + MmeSockOpts = maps:get(sockopts, S#state.mme_conn_cfg), + MmeConnCfg = #{laddr => maps:get(laddr, MmeInfo), + raddr => maps:get(raddr, MmeInfo), + rport => maps:get(rport, MmeInfo), + sockopts => MmeSockOpts}, + {ok, Sock} = sctp_client:connect(MmeConnCfg), + {next_state, ?FUNCTION_NAME, %% loop transition to enable state_timeout + S#state{sock = Sock, + mme_aid = undefined, + mme_name = maps:get(name, MmeInfo), + mme_conn_cfg = MmeConnCfg}, + [{state_timeout, 2_000, conn_est_timeout}]}; + error -> + ?LOG_ERROR("Failed to select an MME"), + {stop, {shutdown, mme_select_error}} + end;
%% Handle connection establishment timeout -connecting(state_timeout, conn_est_timeout, _S) -> - {stop, {shutdown, conn_est_timeout}}; +connecting(state_timeout, conn_est_timeout, + #state{mme_name = MmeName}) -> + ?LOG_ERROR("Timeout establishing connection with MME ~p", [MmeName]), + %% re-enter the state to try again (or another MME) + repeat_state_and_data;
%% Handle PDUs coming from the eNB connecting(cast, {send_data, Data}, _S) -> @@ -202,7 +231,8 @@ {next_state, wait_s1setup_rsp, S#state{mme_aid = Aid}}; _ -> ?LOG_NOTICE("MME connection establishment failed: ~p", [ConnState]), - {stop, {shutdown, conn_est_fail}} + %% re-enter the state to try again (or another MME) + repeat_state_and_data end;
connecting(Event, EventData, S) -> @@ -216,9 +246,10 @@ [{state_timeout, 5_000, s1setup_rsp_timeout}]};
%% Handle S1 SETUP RESPONSE timeout -wait_s1setup_rsp(state_timeout, s1setup_rsp_timeout, _S) -> - ?LOG_ERROR("Timeout waiting for S1 SETUP RESPONSE from MME"), - {stop, {shutdown, s1setup_rsp_timeout}}; +wait_s1setup_rsp(state_timeout, s1setup_rsp_timeout, S) -> + ?LOG_ERROR("Timeout waiting for S1 SETUP RESPONSE from the MME"), + %% re-enter state 'connecting' to try again (or another MME) + {next_state, connecting, S};
%% Handle PDUs coming from the eNB wait_s1setup_rsp(cast, {send_data, Data}, _S) -> @@ -243,13 +274,16 @@ {next_state, connected, S}; {{?'id-S1Setup', unsuccessfulOutcome}, _IEs} -> ?LOG_NOTICE("Rx S1 SETUP FAILURE from MME"), - sctp_send_from_mme(Data, S), - {stop, {shutdown, s1setup_error}}; + %% XXX: sctp_send_from_mme(Data, S), + %% re-enter state 'connecting' to try again (or another MME) + {next_state, connecting, S}; {{Proc, Type}, IEs} -> ?LOG_ERROR("Rx unexpected S1AP PDU from MME: ~p/~p, ~p", [Proc, Type, IEs]), - {stop, {shutdown, s1setup_error}}; + %% re-enter state 'connecting' to try again (or another MME) + {next_state, connecting, S}; {error, _Error} -> - {stop, {shutdown, s1setup_error}} + %% re-enter state 'connecting' to try again (or another MME) + {next_state, connecting, S} end;
%% Handle an #sctp_assoc_change event (MME connection state) @@ -264,7 +298,8 @@ {keep_state, S}; _ -> ?LOG_NOTICE("MME connection state: ~p", [ConnState]), - {stop, {shutdown, conn_fail}} + %% re-enter state 'connecting' to try again (or another MME) + {next_state, connecting, S} end;
wait_s1setup_rsp(Event, EventData, S) -> @@ -353,22 +388,11 @@
terminate(Reason, State, #state{handler = Pid, - enb_handle = Handle, - sock = Sock, - mme_aid = MmeAid}) -> + enb_handle = Handle} = S) -> ?LOG_NOTICE("Terminating in state ~p, reason ~p", [State, Reason]), enb_registry:enb_unregister(Handle), s1ap_proxy:shutdown(Pid), - case Sock of - undefined -> ok; - _ -> - case MmeAid of - undefined -> ok; - _ -> - sctp_common:shutdown({Sock, MmeAid}) - end, - gen_sctp:close(Sock) - end. + close_sock(S).
%% ------------------------------------------------------------------ @@ -455,4 +479,20 @@ end.
+-spec close_sock(state()) -> ok | {error, term()}. +close_sock(#state{sock = undefined}) -> ok; + +close_sock(#state{sock = Sock} = S) -> + close_conn(S), %% terminate the MME connection, if needed + gen_sctp:close(Sock). + + + +-spec close_conn(state()) -> ok | {error, term()}. +close_conn(#state{mme_aid = undefined}) -> ok; + +close_conn(#state{sock = Sock, mme_aid = MmeAid}) -> + sctp_common:shutdown({Sock, MmeAid}). + + %% vim:set ts=4 sw=4 et: diff --git a/src/s1ap_utils.erl b/src/s1ap_utils.erl index 6f7e6b6..d813e76 100644 --- a/src/s1ap_utils.erl +++ b/src/s1ap_utils.erl @@ -68,13 +68,15 @@ s1ap_pdu_info/0]).
+-type tac() :: 0..16#ffff. -type enb_id() :: 0..16#fffffff. -type plmn_id() :: {MCC :: nonempty_string(), MNC :: nonempty_string()}. -type genb_id() :: #{enb_id => enb_id(), plmn_id => plmn_id()}.
--export_type([enb_id/0, +-export_type([tac/0, + enb_id/0, plmn_id/0, genb_id/0]).