From f188fca9b94f68dc195f30bc9b67c09cc000aa94 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 2 Feb 2012 23:52:08 +0100 Subject: add new map utility functiosn and a skeleton AS server --- src/map_as_server.erl | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/map_helper.erl | 120 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 src/map_as_server.erl create mode 100644 src/map_helper.erl diff --git a/src/map_as_server.erl b/src/map_as_server.erl new file mode 100644 index 0000000..86c03af --- /dev/null +++ b/src/map_as_server.erl @@ -0,0 +1,121 @@ + +-module(map_as_server). +-behaviour(gen_server). + +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-export([bind_ac/1, unbind_ac/1, dump/0]). + +-record(ass_state, {sap_sup_pid, as_tbl}). + +-record(ass_record, {ac, user_pid}). + + +% client side + +bind_ac(Srv, AcName) when is_list(AcName) -> + bind_ac(Srv, list_to_tuple(AcName); +bind_ac(Srv, AcName) when is_tuple(AcName) -> + gen_server:call(Srv, {bind_ac, AcName}). + +unbind_ac(Srv, AcName) when is_list(AcName) -> + unbind_ac(Srv, list_to_tuple(AcName); +unbind_ac(Srv, AcName) when is_tuple(AcName) -> + gen_server:call(Srv, {unbind_ac, AcName}). + +dump() -> + fixme. + + +% gen_fsm callbacks + +start_link(Ssn, {M, A, O}) when is_integer(Ssn) -> + ProcName = list_to_atom(?MODULE ++ "_" ++ integer_to_list(Ssn)), + gen_server:start_link({local, ProcName}, ?MODULE, [Ssn, {M, A, O}], []). + +init([Ssn, {M, A, O}]) -> + {ok, SapSupPid} = tcap_sap_sup:start_link(M, A, O), + AssTbl = ets:new(list_to_atom(?MODULE ++ "_" ++ integer_to_list(Ssn)), + [ordered_set, named_table, {keypos, #ass_record.ac}]), + {ok, #ass_state{sap_sup_pid = SapSupPid, as_tbl = AssTbl}}. + + +handle_call({bind_ac, Ac}, {FromPid, _FromRef}, LoopDat) -> + NewRec = #actbl_record{ac = Ac, user_pid = FromPid}, + case ets:insert_new(LoopDat#ass_state.as_tbl, NewRec) of + false -> + {reply, {error, ets_insert}, LoopDat}; + _ -> + link(FromPid), + {reply, ok, LoopDat} + end; + +handle_call({unbind_ac, Ac}, {FromPid, _FromRef}, LoopDat) -> + DelRec = #actbl_record{ac = Ac, user_pid = FromPid}, + ets:delete_object(LoopDat#ass_state.as_tbl, DelRec), + {reply, ok, LoopDat}. + +handle_cast(Info, LoopDat) -> + error_logger:error_report(["unknown handle_cast", + {module, ?MODULE}, {info, Info}, + {state, LoopDat}]), + {noreply, LoopDat}. + +handle_info({'EXIT', Pid, Reason}, LoopDat) -> + io:format("EXIT from process ~p (~p), cleaning up tables~n", + [Pid, Reason]), + ets:match_delete(LoopDat#ass_state.as_tbl, #actbl_record{user_pid = Pid}), + {noreply, LoopDat}; +handle_info(Info, LoopDat) -> + error:logger:error_report(["unknown handle_info", + {module, ?MODULE}, {info, Info}, + {state, LoopDat}]), + {noreply, LoopDat}. + +terminate(Reason, _LoopDat) -> + io:format("terminating ~p with reason ~p~n", [self(), Reason]), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +% server side +as_for_ac(Ac, LoopDat) -> + case ets:lookup(LoopDat#ass_state.as_tbl, Ac) of + [#ass_record{user_pid = UserPid}] -> + {ok, UserPid}; + _ -> + {error, no_such_ac} + end. + +handle_tcap({'TC','BEGIN',indication, + I='TC-BEGIN'{appContextName = Ac, dialogueID = DlgId}}, LoopDat) -> + case as_for_ac(Ac, LoopDat) of + {ok, UserPid} -> + gen_fsm:send_event(UserPid, I); + {error, Reason} -> + error_logger:error_report(["TC-BEGIN for non-existing AC", + {application_context, Ac}]), + ok + end; +handle_tcap({'TC', What, indication, P}) when + What == 'CONTINUE'; What == 'END'; + What == 'U-ABORT'; What == 'P-ABORT'; + What == 'NOTICE' -> + % look up the Pid for the specific Dialogue Handler + case as_for_ac(Ac, LoopDat) of + FIXME FIXME + + ok; +handle_tcap({'TC', 'INVOKE', indication, P}) when + % look up AS for AC, start new invoke in dialogue + +handle_tcap({'TC', What, indication, P}) when + What == 'RESULT-L'; What == 'RESULT-NL'; + What == 'U-ERROR'; What == 'L-CANCEL'; + What == 'L-REJECT'; What == 'U-REJECT'; + What == 'TIMER-RESET' -> + % look up the gen_fsm for the specific Invoke + + ok. + diff --git a/src/map_helper.erl b/src/map_helper.erl new file mode 100644 index 0000000..872cad2 --- /dev/null +++ b/src/map_helper.erl @@ -0,0 +1,120 @@ +% GSM MAP codec wrapper functions + +% (C) 2011-2012 by Harald Welte +% +% All Rights Reserved +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU Affero General Public License as +% published by the Free Software Foundation; either version 3 of the +% License, or (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU Affero General Public License +% along with this program. If not, see . + +-module(map_helper). +-author('Harald Welte '). + +-include_lib("osmo_ss7/include/isup.hrl"). +-include_lib("osmo_map/include/map.hrl"). + +-export([postproc/2, postproc_gt/2, postproc_imsi/2, postproc_msisdn/2]). + +postproc(M=#'UpdateLocationArg'{imsi = Imsi, 'msc-Number' = Msc, 'vlr-Number' = Vlr, + 'v-gmlc-Address' = Gmlc}, Mode) -> + M#'UpdateLocationArg'{imsi = postproc_imsi(Imsi, Mode), + 'msc-Number' = postproc_gt(Msc, Mode), + 'vlr-Number' = postproc_gt(Vlr, Mode), + 'v-gmlc-Address' = postproc_gt(Gmlc, Mode)}; +postproc(M=#'UpdateLocationRes'{'hlr-Number' = Hlr}, Mode) -> + M#'UpdateLocationRes'{'hlr-Number' = postproc_gt(Hlr, Mode)}; +postproc(M=#'UpdateGprsLocationArg'{imsi = Imsi, 'sgsn-Address' = Sgsn}, Mode) -> + M#'UpdateGprsLocationArg'{imsi = postproc_imsi(Imsi, Mode), + 'sgsn-Address' = postproc_gt(Sgsn, Mode)}; +postproc(M=#'UpdateGprsLocationRes'{'hlr-Number' = Hlr}, Mode) -> + M#'UpdateGprsLocationRes'{'hlr-Number' = postproc_gt(Hlr, Mode)}; +postproc(M=#'SendAuthenticationInfoArg'{imsi = Imsi}, Mode) -> + M#'SendAuthenticationInfoArg'{imsi = postproc_imsi(Imsi, Mode)}; +postproc(M=#'ReadyForSM-Arg'{imsi = Imsi}, Mode) -> + M#'ReadyForSM-Arg'{imsi = postproc_imsi(Imsi, Mode)}; +postproc(M=#'MO-ForwardSM-Arg'{imsi = Imsi}, Mode) -> + M#'MO-ForwardSM-Arg'{imsi = postproc_imsi(Imsi, Mode)}; +postproc(M=#'RoutingInfoForSM-Res'{imsi = Imsi}, Mode) -> + M#'RoutingInfoForSM-Res'{imsi = postproc_imsi(Imsi, Mode)}; +postproc(M=#'DeactivateTraceModeArg'{imsi = Imsi}, Mode) -> + M#'DeactivateTraceModeArg'{imsi = postproc_imsi(Imsi, Mode)}; +postproc(M=#'ActivateTraceModeArg'{imsi = Imsi}, Mode) -> + M#'ActivateTraceModeArg'{imsi = postproc_imsi(Imsi, Mode)}; +postproc(M=#'InsertSubscriberDataArg'{imsi = Imsi, msisdn = Msisdn}, Mode) -> + M#'InsertSubscriberDataArg'{imsi = postproc_imsi(Imsi, Mode), + msisdn = postproc_msisdn(Msisdn, Mode)}; +postproc(M=#'AuthenticationFailureReportArg'{imsi = Imsi}, Mode) -> + M#'AuthenticationFailureReportArg'{imsi = postproc_imsi(Imsi, Mode)}; +postproc(M=#'AlertServiceCentreArg'{msisdn = Msisdn, serviceCentreAddress = Smsc}, Mode) -> + M#'AlertServiceCentreArg'{msisdn = postproc_msisdn(Msisdn, Mode), + serviceCentreAddress = postproc_gt(Smsc, Mode)}; +postproc(M=#'RoutingInfoForSM-Arg'{msisdn = Msisdn, serviceCentreAddress = Smsc}, Mode) -> + M#'RoutingInfoForSM-Arg'{msisdn = postproc_msisdn(Msisdn, Mode), + serviceCentreAddress = postproc_gt(Smsc, Mode)}; +postproc(M=#'ProvideRoamingNumberArg'{imsi = Imsi, msisdn = Msisdn, + 'gmsc-Address' = Gmsc}, Mode) -> + M#'ProvideRoamingNumberArg'{imsi = postproc_imsi(Imsi, Mode), + msisdn = postproc_msisdn(Msisdn, Mode), + 'gmsc-Address' = postproc_gt(Gmsc, Mode)}; +postproc(M=#'SendRoutingInfoRes'{msisdn = Msisdn}, Mode) -> + M#'SendRoutingInfoRes'{msisdn = postproc_msisdn(Msisdn, Mode)}; +postproc(M=#'SendRoutingInfoArg'{msisdn = Msisdn, 'gmsc-OrGsmSCF-Address' = Gmsc}, Mode) -> + M#'SendRoutingInfoArg'{msisdn = postproc_msisdn(Msisdn, Mode), + 'gmsc-OrGsmSCF-Address' = postproc_gt(Gmsc, Mode)}; +postproc(M=#'USSD-Arg'{msisdn = Msisdn}, Mode) -> + M#'USSD-Arg'{msisdn = postproc_msisdn(Msisdn, Mode)}; +postproc(M=#'ReportSM-DeliveryStatusArg'{msisdn = Msisdn, + serviceCentreAddress = Smsc}, Mode) -> + M#'ReportSM-DeliveryStatusArg'{msisdn = postproc_msisdn(Msisdn, Mode), + serviceCentreAddress = postproc_gt(Smsc, Mode)}; +postproc(M=#'AnyTimeInterrogationArg'{'gsmSCF-Address' = Scf}, Mode) -> + M#'AnyTimeInterrogationArg'{'gsmSCF-Address' = postproc_gt(Scf, Mode)}; +postproc(M, _Mode) -> + M. + + +postproc_gt(In, post) when is_binary(In) -> + postproc_gt(binary_to_list(In), post); +postproc_gt(asn1_NOVALUE, post) -> + undefined; +postproc_gt(In, post) -> + map_codec:parse_addr_string(In); +postproc_gt(undefined, pre) -> + asn1_NOVALUE; +postproc_gt(In, pre) when is_record(In, party_number) -> + map_codec:encode_addr_string(In); +postproc_gt(In, pre) -> + In. + +postproc_imsi(asn1_NOVALUE, post) -> + undefined; +postproc_imsi(In, post) -> + map_codec:parse_map_addr(In); +postproc_imsi(undefined, pre) -> + asn1_NOVALUE; +postproc_imsi([], pre) -> + asn1_NOVALUE; +postproc_imsi(In, pre) -> + map_codec:encode_map_tbcd(In). + +postproc_msisdn(asn1_NOVALUE, post) -> + undefined; +postproc_msisdn(In, post) -> + map_codec:parse_map_addr(In); +postproc_msisdn(undefined, pre) -> + asn1_NOVALUE; +postproc_msisdn([], pre) -> + asn1_NOVALUE; +postproc_msisdn(In, pre) -> + map_codec:encode_map_tbcd(In). + -- cgit v1.2.3