diff options
| -rw-r--r-- | .gitignore | 10 | ||||
| -rw-r--r-- | LICENSE | 28 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | config.example | 26 | ||||
| -rw-r--r-- | patches/osmo_map.patch | 64 | ||||
| -rw-r--r-- | patches/osmo_sccp.patch | 187 | ||||
| -rw-r--r-- | patches/osmo_ss7.patch | 437 | ||||
| -rw-r--r-- | rebar.config | 7 | ||||
| -rw-r--r-- | src/hex.erl | 33 | ||||
| -rw-r--r-- | src/map_msgs.erl | 590 | ||||
| -rw-r--r-- | src/sms_7bit_encoding.erl | 67 | ||||
| -rw-r--r-- | src/ss7test.app.src | 12 | ||||
| -rw-r--r-- | src/ss7test_app.erl | 544 | ||||
| -rw-r--r-- | src/ss7test_app.hrl | 6 | ||||
| -rw-r--r-- | src/ss7test_helper.erl | 48 | ||||
| -rw-r--r-- | src/ss7test_sup.erl | 27 | ||||
| -rw-r--r-- | ss7test.iml | 20 | 
17 files changed, 2108 insertions, 0 deletions
| diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e46d5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.eunit +deps +*.o +*.beam +*.plt +erl_crash.dump +ebin +rel/example_project +.concrete/DEV_MODE +.rebar @@ -0,0 +1,28 @@ +Copyright (c) 2015, Daniel Mende <mail@c0decafe.de> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +  list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, +  this list of conditions and the following disclaimer in the documentation +  and/or other materials provided with the distribution. + +* Neither the name of ss7MAPer nor the names of its +  contributors may be used to endorse or promote products derived from +  this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae192a0 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# ss7MAPer +SS7 MAP (pen-)testing toolkit diff --git a/config.example b/config.example new file mode 100644 index 0000000..4e0bb82 --- /dev/null +++ b/config.example @@ -0,0 +1,26 @@ +{sctp, [ +    {local_ip, {10,1,2,3}},  +    {local_port, 2905},  +    {remote_ip, {10,1,2,4}},  +    {remote_port, 2905} +]}. +{m3ua, [ +    {local_pc, 10}, +    {remote_pc, 20}, +    {network_ind, 2}, +    {route_ctx, 1}, +    {asp_id, none}, +    {net_appearance, none} +]}. +{sccp, [ +    {gt_local, [1,2,3,4,5,6,7,8,9]} +]}. +{target, [ +    {gt_hlr, [1,2,3,4,5,6,7,8,9,0]}, +    {gt_vlr, [1,2,3,4,5,6,7,8,9,1]}, +    {gt_msc, []}, +    {gt_sgsn, []}, +    {msisdn, [0,9,8,7,6,5,4,3,2,1]}, +    {service_center, [9,8,7,6,5,4,3,2,1]} +    {forward_number, [9,8,7,6,5,4,3,2,0]} +]}. diff --git a/patches/osmo_map.patch b/patches/osmo_map.patch new file mode 100644 index 0000000..255914b --- /dev/null +++ b/patches/osmo_map.patch @@ -0,0 +1,64 @@ +diff --git a/ebin/osmo_map.app b/ebin/osmo_map.app +index 6502f41..6919668 100644 +--- a/ebin/osmo_map.app ++++ b/ebin/osmo_map.app +@@ -4,7 +4,8 @@ + 	 {modules, [map, map_codec, map_helper, + 		    tcap_asn, tcap_helper, + 		    map_only, +-		    map_app_sup, map_ss_server, map_dlg_server]}, ++		    map_app_sup, map_ss_server, map_dlg_server ++            ]}, + 	 {registered, []}, + 	 {applications, []}, + 	 {env, [ +diff --git a/rebar.config b/rebar.config +index c4805bc..c80d3e2 100644 +--- a/rebar.config ++++ b/rebar.config +@@ -1,3 +1,6 @@ + {sub_dirs, ["rel"]}. ++{deps, [ ++        {osmo_ss7, "1", {git, "git://git.osmocom.org/erlang/osmo_ss7/", "master"}} ++    ]}. + {asn1_opts, [ber,verbose]}. + {eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}. +diff --git a/src/map_dlg_server.erl b/src/map_dlg_server.erl +index 08b0fe4..c40ad57 100644 +--- a/src/map_dlg_server.erl ++++ b/src/map_dlg_server.erl +@@ -3,7 +3,7 @@ +  + -behaviour(gen_server). +  +--include_lib("TCAP/include/tcap.hrl"). ++-include_lib("tcap.hrl"). +  + -export([init/1, handle_cast/2, code_change/3, handle_call/3, handle_info/2, terminate/2]). +  +diff --git a/src/map_helper.erl b/src/map_helper.erl +index ea1e291..31da750 100644 +--- a/src/map_helper.erl ++++ b/src/map_helper.erl +@@ -35,7 +35,7 @@ + -author('Harald Welte <laforge@gnumonks.org>'). +  + -include_lib("osmo_ss7/include/isup.hrl"). +--include_lib("osmo_map/include/map.hrl"). ++-include("map.hrl"). + -include_lib("osmo_map/include/map_operations.hrl"). +  + -export([postproc/2, postproc_gt/2, postproc_imsi/2, postproc_msisdn/2, +diff --git a/src/map_ss_server.erl b/src/map_ss_server.erl +index 005cd8b..7b022a2 100644 +--- a/src/map_ss_server.erl ++++ b/src/map_ss_server.erl +@@ -1,7 +1,7 @@ + -module(map_ss_server). + -author('Harald Welte <laforge@gnumonks.org>'). +  +--include_lib("TCAP/include/tcap.hrl"). ++-include_lib("tcap.hrl"). +  + -behaviour(gen_server). +  diff --git a/patches/osmo_sccp.patch b/patches/osmo_sccp.patch new file mode 100644 index 0000000..9721bbb --- /dev/null +++ b/patches/osmo_sccp.patch @@ -0,0 +1,187 @@ +diff --git a/ebin/osmo_sccp.app b/ebin/osmo_sccp.app +index 42d396e..e771403 100644 +--- a/ebin/osmo_sccp.app ++++ b/ebin/osmo_sccp.app +@@ -9,7 +9,19 @@ + 			sccp_scoc, + 			sccp_user, + 			osmo_sccp_tcap, +-			sccp_ssn_dump ++			sccp_ssn_dump, ++            'DialoguePDUs', ++            'Remote-Operations-Generic-ROS-PDUs', ++            'Remote-Operations-Information-Objects', ++            'Remote-Operations-Useful-Definitions', ++            'TC-TMP', ++            'TC-Testing-User', ++            'TCAP-Examples', ++            'TCAP-Tools', ++            'TCAPMessages', ++            'TR', ++            'UnidialoguePDUs' ++ + 		]}, + 	 {registered, [osmo_sccp_app]}, + 	 {mod, {osmo_sccp_app, []}}, +diff --git a/rebar.config b/rebar.config +index 7851051..87c0d28 100644 +--- a/rebar.config ++++ b/rebar.config +@@ -1,3 +1,6 @@ + {erl_opts, [debug_info]}. +-{sub_dirs, ["rel"]}.  ++{sub_dirs, ["rel"]}. ++{deps, [ ++        {osmo_ss7, "1", {git, "git://git.osmocom.org/erlang/osmo_ss7/", "master"}} ++    ]}. + {eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}. +diff --git a/src/osmo_sccp_tcap.erl b/src/osmo_sccp_tcap.erl +index 96cc32f..42fe8d4 100644 +--- a/src/osmo_sccp_tcap.erl ++++ b/src/osmo_sccp_tcap.erl +@@ -9,7 +9,7 @@ +  + -include_lib("osmo_ss7/include/osmo_util.hrl"). + -include_lib("osmo_ss7/include/sccp.hrl"). +--include_lib("TCAP/include/sccp.hrl"). ++-include("sccp.hrl"). +  + %% callbacks needed for gen_server behaviour + -export([init/1, handle_call/3, handle_cast/2, handle_info/2, +diff --git a/src/sccp_scrc.erl b/src/sccp_scrc.erl +index e103172..6872d2b 100644 +--- a/src/sccp_scrc.erl ++++ b/src/sccp_scrc.erl +@@ -31,7 +31,7 @@ +  + -module(sccp_scrc). + -behaviour(gen_fsm). +--export([start_link/1, init/1, terminate/3, idle/2, handle_info/3]). ++-export([start_link/1, init/1, stop/0, terminate/3, idle/2, handle_info/3, handle_event/3]). +  + -include_lib("osmo_ss7/include/osmo_util.hrl"). + -include_lib("osmo_ss7/include/sccp.hrl"). +@@ -42,7 +42,8 @@ + -record(scrc_state, { + 		scoc_conn_ets, + 		next_local_ref, +-		sup_pid	% pid() of the supervisor ++		sup_pid,	% pid() of the supervisor ++        ni + 	}). + % TODO: Integrate with proper SCCP routing / GTT implementation +  +@@ -68,11 +69,15 @@ start_link(InitData) -> + 	gen_fsm:start_link({local, sccp_scrc}, sccp_scrc,  + 			   [{sup_pid,self()}|InitData], [{debug, []}]). +  ++stop() -> ++    gen_fsm:send_all_state_event(?MODULE, stop). ++ + % gen_fsm init callback, called by start_link() + init(InitPropList) -> + 	io:format("SCRC Init PropList~p ~n", [InitPropList]), + 	UserPid = proplists:get_value(sup_pid, InitPropList), +-	LoopData = #scrc_state{sup_pid = UserPid, next_local_ref = 0}, ++	LoopData = #scrc_state{sup_pid = UserPid, next_local_ref = 0, ++            ni = proplists:get_value(ni, InitPropList)}, + 	TableRef = ets:new(scoc_by_ref, [set]), + 	put(scoc_by_ref, TableRef), + 	ok = ss7_links:bind_service(?MTP3_SERV_SCCP, "osmo_sccp"), +@@ -183,7 +188,7 @@ idle(#primitive{subsystem = 'MTP', gen_name = 'TRANSFER', + 	case sccp_routing:route_mtp3_sccp_in(Mtp3) of + 		{remote, SccpMsg2, LsName, Dpc} -> + 			io:format("routed to remote?!?~n"), +-			{ok, M3} = create_mtp3_out(SccpMsg2, LsName, Dpc), ++			{ok, M3} = create_mtp3_out(SccpMsg2, LsName, Dpc, LoopDat#scrc_state.ni), + 			% generate a MTP-TRANSFER.req primitive to the lower layer + 			send_mtp_transfer_down(M3, LsName), + 			LoopDat1 = LoopDat; +@@ -241,13 +246,16 @@ idle(#primitive{subsystem = 'OCRC', gen_name = 'CONNECTION', + 	LoopDat2 = send_sccp_local_out(LoopDat, SccpMsg), + 	{next_state, idle, LoopDat2}. +  ++handle_event(stop, _StateName, StateData) -> ++    {stop, normal, StateData}. ++ + send_mtp_transfer_down(Mtp3) when is_record(Mtp3, mtp3_msg) -> + 	ss7_links:mtp3_tx(Mtp3). +  + send_mtp_transfer_down(Mtp3, LsName) when is_record(Mtp3, mtp3_msg) -> + 	ss7_links:mtp3_tx(Mtp3, LsName). +  +-create_mtp3_out(SccpMsg, LsName, Dpc) when is_record(SccpMsg, sccp_msg) -> ++create_mtp3_out(SccpMsg, LsName, Dpc, Ni) when is_record(SccpMsg, sccp_msg) -> + 	case Dpc of + 	    undefined -> + 		{error, dpc_undefined}; +@@ -263,7 +271,7 @@ create_mtp3_out(SccpMsg, LsName, Dpc) when is_record(SccpMsg, sccp_msg) -> + 			M3R = #mtp3_routing_label{sig_link_sel = 0, + 				  origin_pc = Opc, + 				  dest_pc = Dpc}, +-			M3 = #mtp3_msg{network_ind = ?MTP3_NETIND_INTERNATIONAL, ++			M3 = #mtp3_msg{network_ind = Ni, + 				       service_ind = ?MTP3_SERV_SCCP, + 				       routing_label = M3R, + 				       payload = SccpEnc}, +@@ -271,21 +279,21 @@ create_mtp3_out(SccpMsg, LsName, Dpc) when is_record(SccpMsg, sccp_msg) -> + 		end + 	end. +  +-create_mtp3_out(SccpMsg, LsName) when is_record(SccpMsg, sccp_msg) -> ++create_mtp3_out(SccpMsg, LsName, Ni) when is_record(SccpMsg, sccp_msg) -> + 	CalledParty = proplists:get_value(called_party_addr, + 					  SccpMsg#sccp_msg.parameters), + 	% we _have_ to have a destination point code here + 	Dpc = CalledParty#sccp_addr.point_code, +-	create_mtp3_out(SccpMsg, LsName, Dpc). ++	create_mtp3_out(SccpMsg, LsName, Dpc, Ni). +  + send_sccp_local_out(LoopDat, SccpMsg) when is_record(SccpMsg, sccp_msg) -> + 	case sccp_routing:route_local_out(SccpMsg) of + 		{remote, SccpMsg2, LsName, Dpc} -> +-			% FIXME: get to MTP-TRANSFER.req +-			{ok, M3} = create_mtp3_out(SccpMsg2, LsName, Dpc), +-			% generate a MTP-TRANSFER.req primitive to the lower layer +-			send_mtp_transfer_down(M3, LsName), +-			LoopDat; ++            % FIXME: get to MTP-TRANSFER.req ++			{ok, M3} = create_mtp3_out(SccpMsg2, LsName, Dpc, LoopDat#scrc_state.ni), ++            % generate a MTP-TRANSFER.req primitive to the lower layer ++			Ret = send_mtp_transfer_down(M3, LsName), ++            LoopDat; + 		{local, SccpMsg2, UserPid} -> + 			deliver_to_scoc_sclc(LoopDat, SccpMsg2, UserPid); + 		{error, Reason} -> +diff --git a/src/sccp_user.erl b/src/sccp_user.erl +index 3f51034..0f508f6 100644 +--- a/src/sccp_user.erl ++++ b/src/sccp_user.erl +@@ -41,7 +41,7 @@ + 	 terminate/2, code_change/3]). +  + % our published API +--export([start_link/0]). ++-export([start_link/0, stop/0]). +  + % client functions, may internally talk to our sccp_user server + -export([bind_ssn/1, bind_ssn/2, unbind_ssn/2, pid_for_ssn/2, local_ssn_avail/2, +@@ -66,6 +66,9 @@ init(_Arg) -> + 				       {keypos, #scu_record.ssn_pc}]), + 	{ok, #scu_state{user_table = UserTbl}}. +  ++stop() -> ++    gen_server:cast(?MODULE, stop). ++ + % client side code +  + bind_ssn(Ssn) when is_integer(Ssn) -> +@@ -143,6 +146,8 @@ handle_call({unbind_ssn, Ssn, Pc}, {FromPid, _FromRef}, S) -> + 	ets:delete_object(Tbl, DelRec), + 	{reply, ok, S}. +  ++handle_cast(stop, State) -> ++    {stop, normal, State}; + handle_cast(Info, S) -> + 	error_logger:error_report(["unknown handle_cast", + 				  {module, ?MODULE}, diff --git a/patches/osmo_ss7.patch b/patches/osmo_ss7.patch new file mode 100644 index 0000000..d71e653 --- /dev/null +++ b/patches/osmo_ss7.patch @@ -0,0 +1,437 @@ +diff --git a/include/osmo_ss7.hrl b/include/osmo_ss7.hrl +index 80dcb3e..8ccdf42 100644 +--- a/include/osmo_ss7.hrl ++++ b/include/osmo_ss7.hrl +@@ -14,7 +14,10 @@ + 	sls		:: non_neg_integer(), + 	local		:: record(sigtran_peer), + 	remote		:: record(sigtran_peer), +-	role		:: role() ++	role		:: role(), ++    asp_id      :: integer(), ++    route_ctx   :: integer(), ++    net_app     :: integer() + }). +  +  +diff --git a/include/sccp.hrl b/include/sccp.hrl +index 46b4c11..58036cc 100644 +--- a/include/sccp.hrl ++++ b/include/sccp.hrl +@@ -66,6 +66,8 @@ + -define(SCCP_SSN_RES_NAT,	2#000001100). + -define(SCCP_SSN_BISDN,		2#000001101). + -define(SCCP_SSN_TC_TEST,	2#000001110). ++-define(SCCP_SSN_SGSN,      2#010010101). ++-define(SCCP_SSN_CAP,       2#010010010). +  + % According to Q.731 Section 3.4.2.3.1 + -define(SCCP_NAI_SUBSCRIBER,	2#00000001). +diff --git a/src/m3ua_codec.erl b/src/m3ua_codec.erl +index 5b29b7e..e5976e8 100644 +--- a/src/m3ua_codec.erl ++++ b/src/m3ua_codec.erl +@@ -86,11 +86,17 @@ parse_m3ua_opt(Opt, Msg) -> +  +  + encode_m3ua_msg(#m3ua_msg{version = Version, msg_class = MsgClass, ++			  msg_type = ?M3UA_MSGT_ASPSM_ASPUP, payload = OptList}) -> ++	OptBin = encode_m3ua_opts(OptList), ++    MsgLen = byte_size(OptBin) + 8, ++	<<Version:8, 0:8, MsgClass:8, ?M3UA_MSGT_ASPSM_ASPUP:8, MsgLen:32/big, OptBin/binary>>; ++encode_m3ua_msg(#m3ua_msg{version = Version, msg_class = MsgClass, + 			  msg_type = MsgType, payload = OptList}) -> + 	OptBin = encode_m3ua_opts(OptList), +-	MsgLen = byte_size(OptBin) + 8, ++    MsgLen = byte_size(OptBin) + 8, + 	<<Version:8, 0:8, MsgClass:8, MsgType:8, MsgLen:32/big, OptBin/binary>>. +  ++ + encode_m3ua_opts(OptList) when is_list(OptList) -> + 	encode_m3ua_opts(OptList, <<>>). +  +@@ -114,6 +120,8 @@ encode_m3ua_opt(?M3UA_IEI_PROTOCOL_DATA, Mtp3) when is_record(Mtp3, mtp3_msg) -> + 	end, + 	PayBin = <<Opc:32/big, Dpc:32/big, Si:8, Ni:8, MpD:8, Sls:8, Payload/binary>>, + 	encode_m3ua_opt(?M3UA_IEI_PROTOCOL_DATA, PayBin); ++encode_m3ua_opt(Iei, Data) when is_integer(Iei), is_binary(Data), Data == <<>> -> ++	<<>>; + encode_m3ua_opt(Iei, Data) when is_integer(Iei), is_binary(Data) -> + 	Length = byte_size(Data) + 4, + 	PadLen = get_num_pad_bytes(Length), +diff --git a/src/m3ua_core.erl b/src/m3ua_core.erl +index 2adc424..4198cb7 100644 +--- a/src/m3ua_core.erl ++++ b/src/m3ua_core.erl +@@ -40,7 +40,7 @@ + -include("sccp.hrl"). + -include("m3ua.hrl"). +  +--export([start_link/1]). ++-export([start_link/1, stop/0]). +  + -export([init/1, terminate/3, code_change/4, handle_event/3, handle_info/3]). +  +@@ -59,11 +59,18 @@ + 	  sctp_remote_ip, + 	  sctp_remote_port, + 	  sctp_sock, +-	  sctp_assoc_id ++	  sctp_assoc_id, ++      asp_id, ++      net_app, ++      route_ctx + 	}). +  + start_link(InitOpts) -> +-	gen_fsm:start_link(?MODULE, InitOpts, [{debug, [trace]}]). ++	%~ gen_fsm:start_link(?MODULE, InitOpts, [{debug, [trace]}]). ++	gen_fsm:start_link(?MODULE, InitOpts, []). ++ ++stop() -> ++    gen_fsm:send_all_state_event(?MODULE, stop). +  + reconnect_sctp(L = #m3ua_state{sctp_remote_ip = Ip, sctp_remote_port = Port, sctp_sock = Sock}) -> + 	timer:sleep(1*1000), +@@ -91,13 +98,24 @@ build_openopts(PropList) -> + 	[{active, once}, {reuseaddr, true}] ++ + 	lists:flatten(lists:map(fun build_openopt/1, PropList)). +  ++build_extarg(Arg, _) when Arg == none -> ++    <<>>; ++build_extarg(Arg, Len) -> ++    <<Arg:Len>>. ++ + init(InitOpts) -> + 	{ok, SctpSock} = gen_sctp:open(build_openopts(InitOpts)), ++    Asp_id = proplists:get_value(asp_id, InitOpts), ++    Route_ctx = proplists:get_value(route_ctx, InitOpts), ++    Net_app = proplists:get_value(net_app, InitOpts), + 	LoopDat = #m3ua_state{role = asp, sctp_sock = SctpSock, + 				user_fun = proplists:get_value(user_fun, InitOpts), + 				user_args = proplists:get_value(user_args, InitOpts), + 				sctp_remote_ip = proplists:get_value(sctp_remote_ip, InitOpts), +-				sctp_remote_port = proplists:get_value(sctp_remote_port, InitOpts)}, ++				sctp_remote_port = proplists:get_value(sctp_remote_port, InitOpts), ++                asp_id = build_extarg(Asp_id,32), ++                route_ctx = build_extarg(Route_ctx,32), ++                net_app = build_extarg(Net_app,32)}, + 	LoopDat2 = reconnect_sctp(LoopDat), + 	{ok, asp_down, LoopDat2}. +  +@@ -145,6 +163,8 @@ send_msg_start_tack(LoopDat, State, MsgClass, MsgType, Params) -> +  +  +  ++handle_event(stop, State, LoopDat) -> ++    {stop, normal, LoopDat}; + handle_event(Event, State, LoopDat) -> + 	io:format("Unknown Event ~p in state ~p~n", [Event, State]), + 	{next_state, State, LoopDat}. +@@ -191,13 +211,16 @@ handle_info({sctp, Socket, _RemoteIp, _RemotePort, {ANC, SPC}}, + 			{State, LoopDat}; + 		addr_made_prim -> + 			% FIXME: do we need to change remote_ip in our LoopDat? ++			{State, LoopDat}; ++        addr_confirmed -> ++            % FIXME: do we need to change remote_ip in our LoopDat? + 			{State, LoopDat} + 	end, + 	inet:setopts(Socket, [{active, once}]), + 	{next_state, NewState, LoopDat2}; +  + handle_info({sctp, Socket, RemoteIp, RemotePort, {[Anc], Data}}, State, LoopDat) -> +-	io:format("SCTP rx data: ~p ~p~n", [Anc, Data]), ++	%~ io:format("SCTP rx data: ~p ~p~n", [Anc, Data]), + 	% process incoming SCTP data  + 	if Socket == LoopDat#m3ua_state.sctp_sock, + 	   RemoteIp == LoopDat#m3ua_state.sctp_remote_ip, +@@ -218,13 +241,19 @@ handle_info({sctp, Socket, RemoteIp, RemotePort, {_Anc, Data}}, _State, LoopDat) + 	{next_state, asp_down, LoopDat}. +  +  +- + asp_down(#primitive{subsystem = 'M', gen_name = 'ASP_UP', + 		    spec_name = request, parameters = _Params}, LoopDat) -> + 	% M-ASP_UP.req from user, generate message and send to remote peer +-	send_msg_start_tack(LoopDat, asp_down, ?M3UA_MSGC_ASPSM, ?M3UA_MSGT_ASPSM_ASPUP, []); ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%% ++    % added asp identifier ++    % added network appearance ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%% ++	send_msg_start_tack(LoopDat, asp_down, ?M3UA_MSGC_ASPSM, ?M3UA_MSGT_ASPSM_ASPUP,  ++                [{?M3UA_IEI_ASP_ID, LoopDat#m3ua_state.asp_id}]); + asp_down({timer_expired, t_ack, {?M3UA_MSGC_ASPSM, ?M3UA_MSGT_ASPSM_ASPUP, Params}}, LoopDat) -> +-	send_msg_start_tack(LoopDat, asp_down, ?M3UA_MSGC_ASPSM, ?M3UA_MSGT_ASPSM_ASPUP, Params); ++	send_msg_start_tack(LoopDat, asp_down, ?M3UA_MSGC_ASPSM, ?M3UA_MSGT_ASPSM_ASPUP,  ++                lists:append([{?M3UA_IEI_NET_APPEARANCE, LoopDat#m3ua_state.net_app},  ++                    {?M3UA_IEI_ASP_ID, LoopDat#m3ua_state.asp_id}], Params)); +  + asp_down(#m3ua_msg{msg_class = ?M3UA_MSGC_ASPSM, + 		   msg_type = ?M3UA_MSGT_ASPSM_ASPUP_ACK}, LoopDat) -> +@@ -240,8 +269,14 @@ asp_down(M3uaMsg, LoopDat) when is_record(M3uaMsg, m3ua_msg) -> + asp_inactive(#primitive{subsystem = 'M', gen_name = 'ASP_ACTIVE', + 			spec_name = request, parameters = _Params}, LoopDat) -> + 	% M-ASP_ACTIVE.req from user, generate message and send to remote peer ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%% ++    % added routing context ++    % added network appearance ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%% + 	send_msg_start_tack(LoopDat, asp_inactive, ?M3UA_MSGC_ASPTM, ?M3UA_MSGT_ASPTM_ASPAC, +-			   [{?M3UA_IEI_TRAF_MODE_TYPE, <<0,0,0,1>>}]); ++			   [{?M3UA_IEI_NET_APPEARANCE, LoopDat#m3ua_state.net_app},  ++                {?M3UA_IEI_TRAF_MODE_TYPE, <<0,0,0,2>>}, ++                {?M3UA_IEI_ROUTE_CTX, LoopDat#m3ua_state.route_ctx}]); +  + asp_inactive({timer_expired, t_ack, {?M3UA_MSGC_ASPTM, ?M3UA_MSGT_ASPTM_ASPAC, Params}}, LoopDat) -> + 	send_msg_start_tack(LoopDat, asp_inactive, ?M3UA_MSGC_ASPTM, ?M3UA_MSGT_ASPTM_ASPAC, Params); +@@ -310,7 +345,10 @@ asp_active({timer_expired, t_ack, {?M3UA_MSGC_ASPTM, ?M3UA_MSGT_ASPTM_ASPIA, Par + asp_active(#primitive{subsystem = 'MTP', gen_name = 'TRANSFER', + 		      spec_name = request, parameters = Params}, LoopDat) -> + 	% MTP-TRANSFER.req from user app: Send message to remote peer +-	OptList = [{?M3UA_IEI_PROTOCOL_DATA, Params}], ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%% ++    % added routing context ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%% ++	OptList = [{?M3UA_IEI_ROUTE_CTX, LoopDat#m3ua_state.route_ctx}, {?M3UA_IEI_PROTOCOL_DATA, Params}], + 	Msg = #m3ua_msg{version = 1, msg_class = ?M3UA_MSGC_TRANSFER, + 			msg_type = ?M3UA_MSGT_XFR_DATA, + 			payload = OptList}, +diff --git a/src/sccp_codec.erl b/src/sccp_codec.erl +index 2374d73..9119269 100644 +--- a/src/sccp_codec.erl ++++ b/src/sccp_codec.erl +@@ -326,7 +326,11 @@ encode_sccp_addr(#sccp_addr{res_nat_use = ResNatUse, + 	{PCind, PCbin} = encode_pc(PointCode), + 	ResNatOut = undef_or_true(ResNatUse), + 	RoutIndOut = undef_or_true(RoutInd), +-	<<ResNatOut:1, RoutIndOut:1, GTind:4, SSNind:1, PCind:1, PCbin/binary, SSNbin/binary, GTbin/binary>>. ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++    % removed pointcode ++    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++	%<<ResNatOut:1, RoutIndOut:1, GTind:4, SSNind:1, PCind:1, PCbin/binary, SSNbin/binary, GTbin/binary>>. ++    <<ResNatOut:1, RoutIndOut:1, GTind:4, SSNind:1, 0:1, SSNbin/binary, GTbin/binary>>. +  +  + encode_sccp_opt({AddrTag, AddrVal}) when AddrTag == ?SCCP_PNC_CALLED_PARTY_ADDRESS; +diff --git a/src/sctp_core.erl b/src/sctp_core.erl +index 1e9fc21..730046e 100644 +--- a/src/sctp_core.erl ++++ b/src/sctp_core.erl +@@ -222,7 +222,7 @@ handle_info({sctp, Socket, _RemoteIp, _RemotePort, {ANC, SAC}}, +  + handle_info({sctp, Socket, RemoteIp, RemotePort, {[Anc], Data}}, State, LoopDat) -> + 	Module = LoopDat#sctp_state.module, +-	io:format("SCTP rx data: ~p ~p~n", [Anc, Data]), ++	%~ io:format("SCTP rx data: ~p ~p~n", [Anc, Data]), + 	% process incoming SCTP data + 	if Socket == LoopDat#sctp_state.sctp_sock, + 	   RemoteIp == LoopDat#sctp_state.sctp_remote_ip, +diff --git a/src/ss7_link_m3ua.erl b/src/ss7_link_m3ua.erl +index 15ba837..4d7551d 100644 +--- a/src/ss7_link_m3ua.erl ++++ b/src/ss7_link_m3ua.erl +@@ -41,8 +41,9 @@ + -include_lib("osmo_ss7/include/osmo_ss7.hrl"). +  + -export([start_link/1, init/1]). +- +--export([handle_cast/2]). ++-export([stop/0]). ++-export([handle_cast/2, terminate/2]). ++-export([get_link_state/1]). +  + -record(loop_dat, { + 	 m3ua_pid, +@@ -50,16 +51,20 @@ + 	}). +  + start_link(Args) -> +-	gen_server:start_link(?MODULE, Args, [{debug, [trace]}]). ++	%~ gen_server:start_link(?MODULE, Args, [{debug, [trace]}]). ++	gen_server:start_link(?MODULE, Args, []). +  + init(L = #sigtran_link{type = m3ua, name = Name, linkset_name = LinksetName, +-		       sls = Sls, local = Local, remote = Remote}) -> ++		       sls = Sls, local = Local, remote = Remote, asp_id = Asp_id, ++               route_ctx = Route_ctx, net_app = Net_app}) -> + 	#sigtran_peer{ip = LocalIp, port = LocalPort} = Local, + 	#sigtran_peer{ip = RemoteIp, port = RemotePort} = Remote, + 	% start the M3UA link to the SG + 	Opts = [{user_pid, self()}, + 		{sctp_remote_ip, RemoteIp}, {sctp_remote_port, RemotePort}, + 		{sctp_local_ip, LocalIp}, {sctp_local_port, LocalPort}, ++        {asp_id, Asp_id}, {route_ctx, Route_ctx}, ++        {net_app, Net_app}, + 		{user_fun, fun m3ua_tx_to_user/2}, {user_args, self()}], + 	{ok, M3uaPid} = m3ua_core:start_link(Opts), + 	% FIXME: register this link with SCCP_SCRC +@@ -70,10 +75,16 @@ init(L = #sigtran_link{type = m3ua, name = Name, linkset_name = LinksetName, + %	{ok, ScrcPid} = sccp_scrc:start_link([{mtp_tx_action, {callback_fn, fun scrc_tx_to_mtp/2, M3uaPid}}]), + %	loop(#loop_dat{m3ua_pid = M3uaPid, scrc_pid = ScrcPid}). +  ++stop() -> ++    gen_server:cast(?MODULE, stop). ++ +  + set_link_state(#sigtran_link{linkset_name = LinksetName, sls = Sls}, State) -> + 	ok = ss7_links:set_link_state(LinksetName, Sls, State). +  ++get_link_state(#sigtran_link{linkset_name = LinksetName, sls = Sls}) -> ++    ss7_links:get_link_state(LinksetName, Sls). ++ + scrc_tx_to_mtp(Prim, Args) -> + 	M3uaPid = Args, + 	gen_fsm:send_event(M3uaPid, Prim). +@@ -114,11 +125,14 @@ handle_cast(#primitive{subsystem = 'M', gen_name = 'ASP_INACTIVE'}, L) -> + 	io:format("~p: ASP_INACTIVE.ind~n", [?MODULE]), + 	set_link_state(L#loop_dat.link, up), + 	{noreply, L}; ++handle_cast(stop, State) -> ++    gen_fsm:send_all_state_event(State#loop_dat.m3ua_pid, stop), ++    {stop, normal, State}; + handle_cast(P, L) -> +-	io:format("~p: Ignoring M3UA prim ~p~n", [?MODULE, P]), ++	%~ io:format("~p: Ignoring M3UA prim ~p~n", [?MODULE, P]), + 	{noreply, L}. +  +-terminate(Reason, _S) -> ++terminate(Reason, State) -> + 	io:format("terminating ~p with reason ~p", [?MODULE, Reason]), + 	ok. +  +diff --git a/src/ss7_links.erl b/src/ss7_links.erl +index 3aef33c..794a923 100644 +--- a/src/ss7_links.erl ++++ b/src/ss7_links.erl +@@ -43,11 +43,12 @@ + 	 terminate/2, code_change/3]). +  + % our published API +--export([start_link/0, reload_config/0]). ++-export([start_link/0, reload_config/0, stop/0]). +  + % client functions, may internally talk to our sccp_user server + -export([register_linkset/3, unregister_linkset/1]). + -export([register_link/3, unregister_link/2, set_link_state/3]). ++-export([get_link_state/2]). + -export([bind_service/2, unbind_service/1]). +  + -export([get_pid_for_link/2, get_pid_for_dpc_sls/2, +@@ -95,7 +96,8 @@ + % initialization code +  + start_link() -> +-	gen_server:start_link({local, ?MODULE}, ?MODULE, [], [{debug, [trace]}]). ++	%~ gen_server:start_link({local, ?MODULE}, ?MODULE, [], [{debug, [trace]}]). ++    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). +  + init(_Arg) -> + 	LinksetTbl = ets:new(ss7_linksets, [ordered_set, named_table, +@@ -111,6 +113,11 @@ init(_Arg) -> + 	{ok, #su_state{linkset_tbl = LinksetTbl, link_tbl = LinkTbl, + 			service_tbl = ServiceTbl}}. +  ++stop() -> ++    gen_server:cast(?MODULE, stop). ++ ++ ++     + % client side API +  + % all write operations go through gen_server:call(), as only the ?MODULE +@@ -145,6 +152,9 @@ unregister_link(LinksetName, Sls) -> + set_link_state(LinksetName, Sls, State) -> + 	gen_server:call(?MODULE, {set_link_state, {LinksetName, Sls, State}}). +  ++get_link_state(LinksetName, Sls) -> ++    gen_server:call(?MODULE, {get_link_state, {LinksetName, Sls}}). ++ + -spec bind_service(non_neg_integer(), string()) -> + 					ok | error(). +  +@@ -389,6 +399,15 @@ handle_call({set_link_state, {LsName, Sls, State}}, {FromPid, _}, S) -> + 		{reply, ok, S} + 	end; +  ++handle_call({get_link_state, {LsName, Sls}}, {FromPid, _}, S) -> ++    #su_state{link_tbl = LinkTbl} = S, ++	case ets:lookup(LinkTbl, {LsName, Sls}) of ++	    [] -> ++		{reply, {error, no_such_link}, S}; ++	    [Link] ->  ++        {reply, {ok, Link#slink.state}, S} ++    end; ++ + handle_call({bind_service, {SNum, SName}}, {FromPid, _}, + 	    #su_state{service_tbl = ServTbl} = S) -> + 	NewServ = #service{name = SName, service_nr = SNum, +@@ -408,6 +427,9 @@ handle_call({unbind_service, {SNum}}, {FromPid, _}, + 	ets:delete(ServTbl, SNum), + 	{reply, ok, S}. +  ++ ++handle_cast(stop, State) -> ++    {stop, normal, State}; + handle_cast(Info, S) -> + 	error_logger:error_report(["unknown handle_cast", + 				  {module, ?MODULE}, +@@ -432,8 +454,11 @@ handle_info(Info, S) -> + 				  {info, Info}, {state, S}]), + 	{noreply, S}. +  +-terminate(Reason, _S) -> ++terminate(Reason, S) -> + 	io:format("terminating ~p with reason ~p", [?MODULE, Reason]), ++    ets:delete(S#su_state.linkset_tbl), ++    ets:delete(S#su_state.link_tbl), ++    ets:delete(S#su_state.service_tbl), + 	ok. +  + code_change(_OldVsn, State, _Extra) -> +diff --git a/src/ss7_routes.erl b/src/ss7_routes.erl +index 8965de7..116e172 100644 +--- a/src/ss7_routes.erl ++++ b/src/ss7_routes.erl +@@ -48,10 +48,10 @@ + -include_lib("osmo_ss7/include/mtp3.hrl"). +  + % gen_fsm callbacks +--export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). ++-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +  + % our published API +--export([start_link/0]). ++-export([start_link/0, stop/0]). +  + % client functions, may internally talk to our sccp_user server + -export([create_route/3, delete_route/3, flush_routes/0]). +@@ -71,13 +71,17 @@ + % initialization code +  + start_link() -> +-	gen_server:start_link({local, ?MODULE}, ?MODULE, [], [{debug, [trace]}]). ++	%~ gen_server:start_link({local, ?MODULE}, ?MODULE, [], [{debug, [trace]}]). ++	gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). +  + init(_Arg) -> + 	RouteTbl = ets:new(ss7_routes, [ordered_set, named_table, + 			     {keypos, #ss7route.remote_pc_mask}]), + 	process_flag(trap_exit, true), + 	{ok, #sr_state{route_tbl = RouteTbl}}. ++     ++stop() -> ++    gen_server:cast(?MODULE, stop). +  + % client side API +  +@@ -130,6 +134,9 @@ dump_single_route(#ss7route{remote_pc_mask = {Pc, Mask}, +  + % server side code +  ++handle_cast(stop, State) -> ++    {stop, normal, State}. ++ + handle_call({create_route, {RemotePc, RemoteMask, Name}}, + 				{_FromPid, _FromRef}, S) -> + 	#sr_state{route_tbl = Tbl} = S, diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..06fd2c9 --- /dev/null +++ b/rebar.config @@ -0,0 +1,7 @@ +{erl_opts, [debug_info]}. +{deps, [ +        {osmo_ss7, "1", {git, "git://git.osmocom.org/erlang/osmo_ss7/", "master"}}, +        {osmo_sccp, "1", {git, "git://git.osmocom.org/erlang/osmo_sccp/", "master"}}, +        {osmo_map, "1", {git, "git://git.osmocom.org/erlang/osmo_map/", "master"}} +	]}. +{eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}. diff --git a/src/hex.erl b/src/hex.erl new file mode 100644 index 0000000..8529f95 --- /dev/null +++ b/src/hex.erl @@ -0,0 +1,33 @@ +-module(hex). +-export([bin_to_hexstr/1,hexstr_to_bin/1]). + +hex(N) when N < 10 -> +    $0+N; +hex(N) when N >= 10, N < 16 -> +    $a+(N-10). + +int(C) when $0 =< C, C =< $9 -> +    C - $0; +int(C) when $A =< C, C =< $F -> +    C - $A + 10; +int(C) when $a =< C, C =< $f -> +    C - $a + 10. +     +to_hex(N) when N < 256 -> +    [hex(N div 16), hex(N rem 16)]. +  +list_to_hexstr([]) ->  +    []; +list_to_hexstr([H|T]) -> +    to_hex(H) ++ list_to_hexstr(T). + +bin_to_hexstr(Bin) -> +    list_to_hexstr(binary_to_list(Bin)). + +hexstr_to_bin(S) -> +    list_to_binary(hexstr_to_list(S)). + +hexstr_to_list([X,Y|T]) -> +    [int(X)*16 + int(Y) | hexstr_to_list(T)]; +hexstr_to_list([]) -> +    []. diff --git a/src/map_msgs.erl b/src/map_msgs.erl new file mode 100644 index 0000000..e70b9c7 --- /dev/null +++ b/src/map_msgs.erl @@ -0,0 +1,590 @@ +-module(map_msgs). +-author('Daniel Mende <mail@c0decafe.de>'). + +-include_lib("osmo_map/src/map.hrl"). +-include("ss7test_app.hrl"). + +-compile([export_all]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% default values msgs +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% SCCP Definitions +-define(LOCAL_GLOBAL_TITLE,     [1,2,3,4,5,6,7,8,9]). +-define(REMOTE_GLOBAL_TITLE,    [1,2,3,4,5,6,7,8,0]). +-define(NI,                     2). + +% MAP Definitions +-define(IMSI,                       hex:hexstr_to_bin("01020304050607f8")). +-define(IMSI_AS_NR,                 ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_LAND_MOBILE, +                                        [0,1,0,2,0,3,0,4,0,5,0,6,0,7,8])). +-define(TMSI,                       hex:hexstr_to_bin("1234")). + +-define(MSISDN,                     ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                                        [1,2,3,4,5,6,7,8,9,0])). + +-define(SERVICE_CENTER_ADDRESS,     hex:hexstr_to_bin("010203040506f1")). +-define(SERVICE_CENTER_ADDRESS_OA,  hex:hexstr_to_bin("010203040506f1")). +-define(SERVICE_CENTER_ADDRESS_DA,  hex:hexstr_to_bin("010203040506f1")). +-define(GGSN,                       ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                                        [1,2,3,4,5,6,7,8,9,0])). + + + +% SMS Definitions +-define(ORIGINATING_ADDRESS,        hex:hexstr_to_bin("0b9010203040506f1")). +-define(DESTINATION_ADDRESS,        ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                                        [1,2,3,4,5,6,7,8,9,0])). +-define(SOURCE_ADDRESS,             ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                                        [1,2,3,4,5,6,7,8,9,0])). +-define(SMS_DEST_NR,                [1,2,3,4,5,6,7,8,9,0]). +-define(SMS_SRC_NR,                 [1,2,3,4,5,6,7,8,9,0]). + +-define(IMEI,                       ss7test_helper:encode_phonenumner([1,2,3,4,5,6,7,8,9,0])). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% helper +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_transactionId() -> +    <<1,1,0,0>>. + +build_dialog_request(Context) -> +    build_dialog_request(Context, asn1_NOVALUE). +build_dialog_request(Context, Extra) -> +    DialoguePDU = {'dialogueRequest', +        {'AARQ-apdu', +            [version1], +            Context, +            Extra}}, +    {ok, EncDialoguePDU} = map:encode('DialoguePDU', DialoguePDU), +    EncDialoguePDU. + +encode_map_pdu(Dialog, Local, MapData) -> +    MapPDU = {'begin',                                                              +        {'MapSpecificPDUs_begin', +            get_transactionId(), +            {'EXTERNAL', +                ?'dialogue-as-id', +                asn1_NOVALUE,asn1_NOVALUE, +                {'single-ASN1-type', Dialog}}, +            [{basicROS, +                 {invoke, +                     {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                         {present,1}, +                         asn1_NOVALUE, +                         {local,Local}, +                         MapData}}}]}}, +    {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    EncMapPDU. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SMS +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_testSMSPDU_mt() -> +    SMSText = "ERNW test SMS 31337", +    EncSMSText = 'sms_7bit_encoding':to_7bit(SMSText), +    SMSLen = string:len(SMSText), +    Timestamp = hex:hexstr_to_bin("51803001258080"), +    DA = ss7test_helper:encode_phonenumber(?NUMBER_EXTENSION_NONE, +            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +            ?SMS_SRC_NR), +    <<0:4, 4:4, DA/binary, 0:8, 0:8, Timestamp/binary, SMSLen:8, EncSMSText/binary>>. + +create_testSMSPDU_mo() -> +    SMSText = "ERNW test SMS 31337", +    EncSMSText = 'sms_7bit_encoding':to_7bit(SMSText), +    SMSLen = string:len(SMSText), +    DA = ss7test_helper:encode_phonenumber(?NUMBER_EXTENSION_NONE, +            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +            ?SMS_DEST_NR), +    <<1:8, 23:8, DA/binary, 0:8, 0:8, SMSLen:8, EncSMSText/binary>>. + +create_mt_forwardSM_v2() -> +    create_mt_forwardSM_v2(create_testSMSPDU_mt()). +create_mt_forwardSM_v2(SMSPDU) -> +    MapData = { 'ForwardSM-Arg', +                {imsi, ?IMSI}, +                {serviceCentreAddressOA, ?SERVICE_CENTER_ADDRESS_DA}, +                SMSPDU, +                asn1_NOVALUE, asn1_NOVALUE}, +    Dialog = build_dialog_request({0,4,0,0,1,0,25,2}), +    encode_map_pdu(Dialog, 46, MapData). + +create_mt_forwardSM() -> +    create_mt_forwardSM(create_testSMSPDU_mt()). +create_mt_forwardSM(SMSPDU) -> +    MapData = {'MT-ForwardSM-Arg', +               {serviceCentreAddressDA, ?SERVICE_CENTER_ADDRESS_DA}, +               {msisdn, ?SOURCE_ADDRESS}, +               SMSPDU, +               asn1_NOVALUE,    %moreMessagesToSend +               asn1_NOVALUE     %extensionContainer +               }, +    Dialog = build_dialog_request(?'shortMsgMT-RelayContext-v3'), +    encode_map_pdu(Dialog, 44, MapData). +     +create_mo_forwardSM_v2() -> +    create_mo_forwardSM_v2(create_testSMSPDU_mo()). +create_mo_forwardSM_v2(SMSPDU) -> +    MapData = { 'ForwardSM-Arg', +                {serviceCentreAddressDA, ?SERVICE_CENTER_ADDRESS_DA}, +                {msisdn, ?MSISDN}, +                SMSPDU, +                asn1_NOVALUE, asn1_NOVALUE}, +    Dialog = build_dialog_request({0,4,0,0,1,0,21,2}), +    encode_map_pdu(Dialog, 46, MapData). + +create_mo_forwardSM() -> +    create_mo_forwardSM(create_testSMSPDU_mo()). +create_mo_forwardSM(SMSPDU) -> +    MapData = {'MO-ForwardSM-Arg', +                 {serviceCentreAddressDA, ?SERVICE_CENTER_ADDRESS_DA}, +                 {msisdn, ?MSISDN}, +                 SMSPDU, +                 asn1_NOVALUE, +                 asn1_NOVALUE  % ?IMSI_FROM +                   }, +    Dialog = build_dialog_request(?'shortMsgMO-RelayContext-v3'), +    encode_map_pdu(Dialog, 46, MapData). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% to HLR +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +create_sendRoutingInfoForSM() -> +    create_sendRoutingInfoForSM(?MSISDN, ?SERVICE_CENTER_ADDRESS). +create_sendRoutingInfoForSM(Msisdn, Ssaddr) -> +    MapData = { 'send-routing-info-for-sm-arg', +                Msisdn, +                false, +                Ssaddr, +                asn1_NOVALUE, +                asn1_NOVALUE, %true, +                asn1_NOVALUE, %0, +                asn1_NOVALUE, %hex:hexstr_to_bin("0b912374140404f7"), +                asn1_NOVALUE}, +    MapPDU = {'begin', +              {'MapSpecificPDUs_begin', +               get_transactionId(),  +               {'EXTERNAL', +                {0,0,17,773,1,1,1}, +                asn1_NOVALUE, +                asn1_NOVALUE, +                {'single-ASN1-type',<<96,11,161,9,6,7,4,0,0,1,0,20,3>>}},  +               [{'basicROS',  +                 {'invoke', +                  {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                   {'present', 1}, +                   asn1_NOVALUE,  +                   {'local', 45},  +                   MapData}}}]}}, +    {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    EncMapPDU. + +create_sendRoutingInfo() -> +    create_sendRoutingInfo(?MSISDN, ?LOCAL_GLOBAL_TITLE). +create_sendRoutingInfo(Msisdn, OrGsmSCF) -> +    MapData = {'SendRoutingInfoArg', +                Msisdn, +                asn1_NOVALUE, +                asn1_NOVALUE, +                basicCall, +                asn1_NOVALUE, +                asn1_NOVALUE, +                ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                        OrGsmSCF),     %gsmc-OrGsmSCF-Address +                <<7,0,1,68,40,9,150,31>>,   %callReferenceNumber +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                {'CamelInfo', +                    [phase1,phase2,phase3,phase4], +                    asn1_NOVALUE, +                    asn1_NOVALUE, +                    ['o-csi','d-csi','t-csi']}, +                asn1_NOVALUE, +                {'ExtensionContainer', +                    [{'PrivateExtension',{1,2,826,0,1249,58,1,0},<<164,5,48,3,129,1,6>>}, %Nokia ExtensionType Extension +                     {'PrivateExtension',{0,34,5},<<224,2,132,0>>}],    %Nokia srbtSupportIndicator +                    asn1_NOVALUE}, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, %'NULL', +                asn1_NOVALUE, +                asn1_NOVALUE, %'NULL', +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE}, +    MapPDU = {'begin', +        {'MapSpecificPDUs_begin', +            get_transactionId(),  +            {'EXTERNAL', +                {0,0,17,773,1,1,1}, +                asn1_NOVALUE,asn1_NOVALUE, +                {'single-ASN1-type', +                    <<96,15,128,2,7,128,161,9,6,7,4,0,0,1,0,5,3>>}}, +            [{basicROS, +                 {invoke, +                     {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                         {present,1}, +                         asn1_NOVALUE, +                         {local,22}, +                         MapData}}}]}}, +    {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    EncMapPDU. + +%~ create_sendRoutingInfoForGprs() -> +    %~ MapData = {'SendRoutingInforForGprsArg', +                %~ ?IMSI, +                %~ asn1_NOVALUE, +                %~ ?GGSN}, +    %~ MapPDU = {'begin', +        %~ {'MapSpecificPDUs_begin', +            %~ get_transactionId(),  +            %~ {'EXTERNAL', +                %~ {0,0,17,773,1,1,1}, +                %~ asn1_NOVALUE,asn1_NOVALUE, +                %~ {'single-ASN1-type',<<96,15,128,2,7,128,161,9,6,7,4,0,0,1,0,33,4>>}}, +            %~ [{basicROS, +                 %~ {invoke, +                     %~ {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                         %~ {present,1}, +                         %~ asn1_NOVALUE, +                         %~ {local,24}, +                         %~ MapData}}}]}}, +    %~ {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    %~ EncMapPDU. + +%~ create_registerSS() -> +    %~ create_registerSS(?IMSI, ?DESTINATION_ADDRESS). +create_registerSS(Imsi, Origin, DestinationNumber) -> +    MapDialoguePDU = {'map-open', +                    {'MAP-OpenInfo', +                        ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_LAND_MOBILE, +                            Imsi),    %destinationReference +                        ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                            Origin),   %originationReference +                        asn1_NOVALUE }}, +    {ok, EncMapDialoguePDU} = map:encode('MAP-DialoguePDU', MapDialoguePDU), +    DialoguePDU = {'dialogueRequest', +                    {'AARQ-apdu', +                        [version1], +                        ?'networkFunctionalSsContext-v2', %?'ss-InvocationNotificationContext-v3', +                        [{'EXTERNAL', +                            ?'map-DialogueAS', +                            asn1_NOVALUE,asn1_NOVALUE, +                            {'single-ASN1-type', +                                EncMapDialoguePDU}}]}}, +    {ok, EncDialoguePDU} = map:encode('DialoguePDU', DialoguePDU), +    MapData = {'RegisterSS-Arg', +                ?allForwardingSS,    %ss-Code +                asn1_NOVALUE, +                DestinationNumber,   %forwardedToNumber +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE}, +    MapPDU = {'begin',                            +        {'MapSpecificPDUs_begin', +            get_transactionId(), +            {'EXTERNAL', +                ?'dialogue-as-id', +                asn1_NOVALUE,asn1_NOVALUE, +                {'single-ASN1-type', +                    EncDialoguePDU +                    }}, +            [{basicROS, +                 {invoke, +                     {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                         {present,1}, +                         asn1_NOVALUE, +                         {local,10}, +                         MapData}}}]}}, +    {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    EncMapPDU. + +create_eraseSS(Imsi, Origin) -> +    MapDialoguePDU = {'map-open', +                    {'MAP-OpenInfo', +                        ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_LAND_MOBILE, +                            Imsi),    %destinationReference +                        ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                            Origin),   %originationReference +                        asn1_NOVALUE }}, +    {ok, EncMapDialoguePDU} = map:encode('MAP-DialoguePDU', MapDialoguePDU), +    DialoguePDU = {'dialogueRequest', +                    {'AARQ-apdu', +                        [version1], +                        ?'networkFunctionalSsContext-v2', %?'ss-InvocationNotificationContext-v3', +                        [{'EXTERNAL', +                            ?'map-DialogueAS', +                            asn1_NOVALUE,asn1_NOVALUE, +                            {'single-ASN1-type', +                                EncMapDialoguePDU}}]}}, +    {ok, EncDialoguePDU} = map:encode('DialoguePDU', DialoguePDU), +    MapData = {'SS-ForBS-Code-Arg', +                ?allForwardingSS,   %ss-Code +                asn1_NOVALUE,       %basicService +                asn1_NOVALUE},      %longFTN-Supported +    MapPDU = {'begin',                            +        {'MapSpecificPDUs_begin', +            get_transactionId(), +            {'EXTERNAL', +                ?'dialogue-as-id', +                asn1_NOVALUE,asn1_NOVALUE, +                {'single-ASN1-type', +                    EncDialoguePDU +                    }}, +            [{basicROS, +                 {invoke, +                     {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                         {present,1}, +                         asn1_NOVALUE, +                         {local,11}, +                         MapData}}}]}}, +    {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    EncMapPDU. + +create_updateLocation() -> +    create_updateLocation(?IMSI, ?LOCAL_GLOBAL_TITLE, ?LOCAL_GLOBAL_TITLE). +create_updateLocation(Imsi, MscNr, VlrNr) -> +    MapData = {'UpdateLocationArg', +                Imsi, +                ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                        MscNr),     %msc-Number +                ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                        VlrNr),     %vlr-Number +                asn1_NOVALUE, %<<59,151,2,0>>,             %lmsi +                asn1_NOVALUE, +                {'VLR-Capability', +                    [phase1,phase2,phase3,phase4], +                    asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, +                    asn1_NOVALUE,'NULL', +                    [lcsCapabilitySet1,lcsCapabilitySet2], +                    ['o-csi','d-csi','vt-csi','mt-sms-csi'], +                    asn1_NOVALUE,asn1_NOVALUE}, +                asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, +                asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, +                'NULL'}, +    MapPDU = {'begin', +        {'MapSpecificPDUs_begin', +            get_transactionId(), +            {'EXTERNAL', +                ?'dialogue-as-id', +                asn1_NOVALUE,asn1_NOVALUE, +                {'single-ASN1-type', +                    <<96,15,128,2,7,128,161,9,6,7,4,0,0,1,0,1,3>>}}, +            [{basicROS, +                 {invoke, +                     {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                         {present,1}, +                         asn1_NOVALUE, +                         {local,2}, +                         MapData}}}]}}, +    {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    EncMapPDU. +     +create_anyTimeInerrogation() -> +    create_anyTimeInerrogation(?IMSI, ?LOCAL_GLOBAL_TITLE). +create_anyTimeInerrogation(Imsi, GsmSCF) -> +    MapData = {'AnyTimeInterrogationArg', +                 {imsi, Imsi}, %{msisdn,?MSISDN}, +                 {'RequestedInfoMAP-MS-DataTypes',asn1_NOVALUE, +                     'NULL',asn1_NOVALUE,asn1_NOVALUE, +                     asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, +                     asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}, +                 ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                        GsmSCF),     %gsmSCF-Address +                 asn1_NOVALUE}, +    Dialog = build_dialog_request(?'anyTimeInfoEnquiryContext-v3'), +    encode_map_pdu(Dialog, 71, MapData). + +create_sendAuthenticationInfo() -> +    create_sendAuthenticationInfo(?IMSI). +create_sendAuthenticationInfo(Imsi) -> +    create_sendAuthenticationInfo(Imsi, 5). +create_sendAuthenticationInfo(Imsi, Nr) -> +    MapData = { 'SendAuthenticationInfoArg', +                Imsi, +                Nr, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE, +                asn1_NOVALUE}, +    MapPDU = {'begin', +        {'MapSpecificPDUs_begin', +            get_transactionId(),  +            {'EXTERNAL', +                ?'dialogue-as-id', +                asn1_NOVALUE,asn1_NOVALUE, +                {'single-ASN1-type',<<96,11,161,9,6,7,4,0,0,1,0,14,3>>}}, +            [{basicROS, +                 {invoke, +                     {'MapSpecificPDUs_begin_components_SEQOF_basicROS_invoke', +                         {present,1}, +                         asn1_NOVALUE, +                         {local,56}, +                         MapData}}}]}}, +    {ok, EncMapPDU} = map:encode('MapSpecificPDUs', MapPDU), +    EncMapPDU. + +create_sendImsi() -> +    create_sendImsi(?MSISDN). +create_sendImsi(Msisdn) -> +    MapData = Msisdn, +    Dialog = build_dialog_request(?'imsiRetrievalContext-v2'), +    encode_map_pdu(Dialog, 58, MapData). + +create_purgeMs() -> +    create_purgeMs(?IMSI, ?LOCAL_GLOBAL_TITLE). +create_purgeMs(Imsi, VlrNr) -> +    MapData = {'PurgeMs-Arg', +                Imsi, +                ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                        ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                        VlrNr),   %VLR number +                asn1_NOVALUE,   %SGSN number +                asn1_NOVALUE},  %Last Known Location +    Dialog = build_dialog_request(?'msPurgingContext-v3'), +    encode_map_pdu(Dialog, 67, MapData). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% to MSC/VLR +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_provideSubscriberInfo() -> +    MapData = {'ProvideSubscriberInfoArg', +         ?IMSI, +         asn1_NOVALUE, +         {'RequestedInfoMAP-MS-DataTypes', +            'NULL',         %locationInformation +            'NULL',   %subscriberState +            asn1_NOVALUE,   %extensionContainer +            'NULL',   %currentLocation +            asn1_NOVALUE,   %requestedDomain +            'NULL',   %imei +            asn1_NOVALUE,   %ms-classmark +            asn1_NOVALUE,   %mnpRequestedInfo +            asn1_NOVALUE,   %t-adsData +            asn1_NOVALUE    %requestedNodes +            }, +        asn1_NOVALUE, +        asn1_NOVALUE}, +    Dialog = build_dialog_request(?'subscriberInfoEnquiryContext-v3'), +    encode_map_pdu(Dialog, 70, MapData). + +create_sendIdentification() -> +    MapData = { 'SendIdentificationArg', +            ?TMSI, +            5,              %NumberOfRequestedVectors +            asn1_NOVALUE,   %segmentationProhibited +            asn1_NOVALUE,   %ExtensionContainer +            asn1_NOVALUE,   %msc-Number +            asn1_NOVALUE,   %previous-LAI +            asn1_NOVALUE    %hopCounter +            }, +    Dialog = build_dialog_request(?'interVlrInfoRetrievalContext-v3'), +    encode_map_pdu(Dialog, 55, MapData). + +create_provideRoamingNumber() -> +    MapData = {'ProvideRoamingNumberArg', +                 ?IMSI, +                 ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                            [1,2,3,4,5,6,7,8,9]),         %msc-Number +                 ?MSISDN, +                 asn1_NOVALUE, +                 {'ExternalSignalInfo','gsm-0408', +                     <<4,1,160>>, +                     asn1_NOVALUE}, +                 asn1_NOVALUE,asn1_NOVALUE, +                 ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                            ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                            [1,2,3,4,5,6,7,8,9,0]),          %gmsc-Address +                 <<112,4,1,54,22,114,55>>,          %callReferenceNumber +                 asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, +                 asn1_NOVALUE, +                 [phase1,phase2,phase3], +                 asn1_NOVALUE,'NULL',asn1_NOVALUE,asn1_NOVALUE, +                 asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, +                 asn1_NOVALUE,asn1_NOVALUE}, +    Dialog = build_dialog_request(?'roamingNumberEnquiryContext-v3'), +    encode_map_pdu(Dialog, 4, MapData). + +create_cancelLocation() -> +    MapData = {'CancelLocationArg', +                 {imsi,?IMSI}, +                 updateProcedure, +                 asn1_NOVALUE, +                 asn1_NOVALUE}, +    Dialog = build_dialog_request(?'locationCancellationContext-v3'),   %v2: {0,4,0,0,1,0,2,2} +    encode_map_pdu(Dialog, 3, MapData). + +create_insertSubscriberData() -> +    MapData = {'InsertSubscriberDataArg', +        asn1_NOVALUE,   %imsi +        asn1_NOVALUE,   %msisdn +        asn1_NOVALUE,   %additional msisdn +        asn1_NOVALUE,   %category +        asn1_NOVALUE,   %subscriber status +        asn1_NOVALUE,   %bearer service list +        asn1_NOVALUE,   %teleservice list +        asn1_NOVALUE,   %forwarding information list +        asn1_NOVALUE,   %call barring information list +        asn1_NOVALUE,   %CUG information list +        asn1_NOVALUE,   %SS-Data list +        asn1_NOVALUE,   %eMLPP subscription data +        asn1_NOVALUE,   %MC-subscription data +        asn1_NOVALUE,   % +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE, +        asn1_NOVALUE +        }, +    Dialog = build_dialog_request('subscriberDataMngtContext-v3'), +    encode_map_pdu(Dialog, 7, MapData). + diff --git a/src/sms_7bit_encoding.erl b/src/sms_7bit_encoding.erl new file mode 100644 index 0000000..4c39b78 --- /dev/null +++ b/src/sms_7bit_encoding.erl @@ -0,0 +1,67 @@ +-module(sms_7bit_encoding). + +-author("dawid.figiel@gmail.com"). +-compile(export_all). + +%% 7-bit encoding  +%% --------------------------------------------------------------------- +%% Function for converting string to 7-bit encoding according to: +%% GSM 03.38 Version 5.3.0 +%% --------------------------------------------------------------------- +%% Initial Function call: to_7bit(String). +%% --------------------------------------------------------------------- +%% Input:  String containing only ASCII characters +%% --------------------------------------------------------------------- +%% Output: Binary encoded String  +%% --------------------------------------------------------------------- +to_7bit([]) -> <<>>; +to_7bit(String) -> to_7bit(list_to_binary(String),<<>>,1). + +to_7bit(<<Char1:8>>,<<>>,_Cntr) -> <<<<Char1>>/binary>>; +to_7bit(<<_Char1:8>>,Out,8) -> Out; +to_7bit(<< Char1:8>>,Out,7) -> +%%    << Out/binary,<<((Char1 bsr 6) bor 26)>>/binary >>;  +    << Out/binary,<<((Char1 bsr 6) bor 64)>>/binary >>;  +to_7bit(<<Char1:8>>,Out,Cntr) ->     +    << Out/binary,<<(Char1 bsr (Cntr - 1))>>/binary >>;  +to_7bit(<<_Char:8,In/binary>>,Out,8)-> +    to_7bit(In,Out,1); +to_7bit(<<Char1:8,Char2:8,In/binary>>,Out,Cntr)-> +    SRChar1 = Char1 bsr (Cntr - 1), +    NewChar1  = <<Char2:Cntr,SRChar1:(8-Cntr)>>, +    to_7bit(<<<<Char2>>/binary,In/binary>>,<<Out/binary,NewChar1/binary>>,Cntr+1). + +%% 7-bit decoding  +%% --------------------------------------------------------------------- +%% Function for converting 7-bit encoding to String according to: +%% GSM 03.38 Version 5.3.0 +%% --------------------------------------------------------------------- +%% Initial Function call: from_7bit(SevenBitEncodedBinary). +%% --------------------------------------------------------------------- +%% Input: Binary encoded String  +%% --------------------------------------------------------------------- +%% Output:  String containing only ASCII characters +%% --------------------------------------------------------------------- +from_7bit(List) when is_list(List) -> from_7bit(list_to_binary(List)); +from_7bit(<<>>) -> []; +from_7bit(Bin) -> +    from_7bit(Bin,<<>>,<<>>,1). + +%%from_7bit(<<>>,<<13>>,Out,8) -> +from_7bit(<<>>,<<32>>,Out,8) -> +    binary_to_list(Out); +from_7bit(<<>>,<<CharN>>,Out,8) -> +    binary_to_list(<<Out/binary,CharN:8>>); +from_7bit(<<>>,<<0>>,Out,_Cntr) -> +    binary_to_list(Out); +from_7bit(<<>>,<<CharN>>,Out,Cntr) -> +    binary_to_list(<<Out/binary,0:(9-Cntr),CharN:(Cntr-1)>>); +from_7bit(<<CharN:1,CharO:7,In/binary>>,<<CharI>>,Out,8) -> +    from_7bit(In,<<CharN>>,<<Out/binary,0:1,CharI:7,0:1,CharO:7>>,2); +from_7bit(<<Byte:8,In/binary>>,<<>>,<<>>,1) -> +    CharN = Byte bsr 7, +    from_7bit(In,<<CharN>>,<<(Byte band 127)>>,2); +from_7bit(<<Byte:8,In/binary>>,<<CharI>>,Out,Cntr) -> +    Char = (Byte bsl (Cntr - 1)) bor CharI, +    CharN = Byte bsr (8 - Cntr), +    from_7bit(In,<<CharN>>,<<Out/binary,0:1,Char:7>>,Cntr+1). diff --git a/src/ss7test.app.src b/src/ss7test.app.src new file mode 100644 index 0000000..cb8c804 --- /dev/null +++ b/src/ss7test.app.src @@ -0,0 +1,12 @@ +{application, ss7test, + [ +  {description, ""}, +  {vsn, "1"}, +  {registered, []}, +  {applications, [ +                  kernel, +                  stdlib +                 ]}, +  {mod, { ss7test_app, ["./config"]}}, +  {env, []} + ]}. diff --git a/src/ss7test_app.erl b/src/ss7test_app.erl new file mode 100644 index 0000000..cc0bb22 --- /dev/null +++ b/src/ss7test_app.erl @@ -0,0 +1,544 @@ +-module(ss7test_app). +-author('Daniel Mende <mail@c0decafe.de>'). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +-include_lib("osmo_ss7/include/osmo_util.hrl"). +-include_lib("osmo_ss7/include/osmo_ss7.hrl"). +-include_lib("osmo_ss7/include/m3ua.hrl"). +-include_lib("osmo_ss7/include/sccp.hrl"). +-include_lib("osmo_ss7/include/isup.hrl"). +-include_lib("osmo_map/src/tcap_asn.hrl"). +-include("ss7test_app.hrl"). + +-define(TRACE, false). + +-record(loop_dat, { +	 scrc_pid, +	 m3ua_pid, +     ss7links_pid, +     ss7routes_pid, +     link, +     local_pc, +     remote_pc, +     gt_local, +     gt_hlr, +     gt_vlr, +     gt_msc, +     gt_sgsn, +     msisdn, +     imsi, +     scenter, +     fnumber +	}). + +%% change routing context in m3ua_code.erl => fixed +%% add pointcode to sccp in sccp_codec.erl +%% change network indicator in sccp_scrc.erl => fixed + + +%% =================================================================== +%% Application callbacks +%% =================================================================== + +start(_, Configfile) -> +    LoopDat = init(Configfile), +    spawn(ss7test_app, loop, [LoopDat]). + +stop(_State) -> +    ok. + +%% =================================================================== + +init(Configfile) -> +    Config = case file:consult(Configfile) of +        {ok, C} -> +            C; +        {error, E} -> +            io:fwrite("\e[91;1mError reading config file ~p:~n~p~n\e[39;49;0m", [Configfile, E]), +            exit(config_error) +        end, +    {sctp, SCTP_Config} = lists:keyfind(sctp, 1, Config), +    {m3ua, M3UA_Config} = lists:keyfind(m3ua, 1, Config), +    {sccp, Sccp} = lists:keyfind(sccp, 1, Config), +    {target, Target} = lists:keyfind(target, 1, Config), +    {gt_local, GT_Local} = lists:keyfind(gt_local, 1, Sccp), +    {gt_hlr, GT_Hlr} = lists:keyfind(gt_hlr, 1, Target), +    {gt_vlr, GT_Vlr} = lists:keyfind(gt_vlr, 1, Target), +    {gt_msc, GT_Msc} = lists:keyfind(gt_msc, 1, Target), +    {gt_sgsn, GT_Sgsn} = lists:keyfind(gt_sgsn, 1, Target), +    {msisdn, Msisdn} = lists:keyfind(msisdn, 1, Target), +    Msisdn_enc = ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                    ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                    Msisdn), +    {service_center, SCenter} = lists:keyfind(service_center, 1, Target), +    SCenter_enc = ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                    ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                    SCenter), +    {forward_number, FNumber} = lists:keyfind(forward_number, 1, Target), +    FNumber_enc = ss7test_helper:encode_msisdn(?NUMBER_EXTENSION_NONE, +                    ?NUMBER_NATURE_INTERNATIONAL, ?NUMBER_PLAN_ISDN, +                    FNumber), +    % start link server and create linkset +    {ok, SS7linksPid} = ss7_links:start_link(), +    sys:trace(ss7_links, ?TRACE), +    {local_pc, Local_PC} = lists:keyfind(local_pc, 1, M3UA_Config), +    {remote_pc, Remote_PC} = lists:keyfind(remote_pc, 1, M3UA_Config), +    ok = ss7_links:register_linkset(Local_PC, Remote_PC, "test_linkset"), +    %start route server and add route +    {ok, SS7routesPid} = ss7_routes:start_link(), +    ok = ss7_routes:create_route(Remote_PC, 16#ffff, "test_linkset"), +    % create m3ua link +    {local_ip, Local_IP} = lists:keyfind(local_ip, 1, SCTP_Config), +    {local_port, Local_Port} = lists:keyfind(local_port, 1, SCTP_Config), +    Local = #sigtran_peer{ip = Local_IP, port = Local_Port}, +    {remote_ip, Remote_IP} = lists:keyfind(remote_ip, 1, SCTP_Config), +    {remote_port, Remote_Port} = lists:keyfind(remote_port, 1, SCTP_Config), +    Remote = #sigtran_peer{ip = Remote_IP, port = Remote_Port}, +    {asp_id, Asp_ID} = lists:keyfind(asp_id, 1, M3UA_Config), +    {route_ctx, Route_CTX} = lists:keyfind(route_ctx, 1, M3UA_Config), +    {net_appearance, Net_Appearance} = lists:keyfind(net_appearance, 1, M3UA_Config), +    Link = #sigtran_link{type = m3ua, name = "test_link", linkset_name = "test_linkset", +                sls = 0, local = Local, remote = Remote, asp_id = Asp_ID, +                route_ctx = Route_CTX, net_app = Net_Appearance}, +    {ok, M3uaPid} = ss7_link_m3ua:start_link(Link), +    % instantiate SCCP routing instance +    {network_ind, Network_Ind} = lists:keyfind(network_ind, 1, M3UA_Config), +	{ok, ScrcPid} = sccp_scrc:start_link([{mtp_tx_action, {callback_fn, fun scrc_tx_to_mtp/2, M3uaPid}}, +                {ni, Network_Ind}]), +    sys:trace(sccp_scrc, ?TRACE), +    {ok, _SccpPid} = sccp_user:start_link(), +    sys:trace(sccp_user, ?TRACE), +    io:format("Waiting for M3UA link comming up...~n"), +    wait_for_link(Link), +    #loop_dat{m3ua_pid = M3uaPid, scrc_pid = ScrcPid, ss7links_pid = SS7linksPid, ss7routes_pid = SS7routesPid, link = Link, +                local_pc = Local_PC, remote_pc = Remote_PC, gt_local = GT_Local, gt_hlr = GT_Hlr, gt_vlr = GT_Vlr, gt_msc = GT_Msc,  +                gt_sgsn = GT_Sgsn, msisdn = Msisdn_enc, scenter = SCenter_enc, fnumber = FNumber_enc}. + +wait_for_link(Link) -> +    case ss7_link_m3ua:get_link_state(Link) of +      {ok, down} -> +        timer:sleep(100), +        wait_for_link(Link); +      {ok, active} -> +        {ok}; +      _ ->  +        {error} +     end. + +loop(L) -> +    receive +		{sccp, Prim} -> +            {primitive, 'N', 'UNITDATA', indication, Data} = Prim, +            {sccp_msg, _, ProtData} = Data, +            {user_data, _UserData} = lists:keyfind(user_data, 1, ProtData), +			%~ io:format("Rx: ~p~n", [map:decode('MapSpecificPDUs', UserData)]), +			loop(L); +        {send, {Gts, {Lssn, Rssn}}, Data} -> +            Tlssn = case Lssn of +                hlr  -> ?SCCP_SSN_HLR; +                vlr  -> ?SCCP_SSN_VLR; +                msc  -> ?SCCP_SSN_MSC; +                sgsn -> ?SCCP_SSN_SGSN; +                cap  -> ?SCCP_SSN_CAP; +                _    -> ?SCCP_SSN_UNKNOWN +            end, +            Trssn = case Rssn of +                hlr  -> ?SCCP_SSN_HLR; +                vlr  -> ?SCCP_SSN_VLR; +                msc  -> ?SCCP_SSN_MSC; +                sgsn -> ?SCCP_SSN_SGSN; +                cap  -> ?SCCP_SSN_CAP; +                _    -> ?SCCP_SSN_UNKNOWN +            end, +            send_tcap(L, Gts, {Tlssn, Trssn}, Data), +            loop(L); +        {test_hlr} -> +            io:format("Testing HLR~n"), +            test_hlr(L), +            loop(L); +        {link_state} -> +            io:format("~p~n", [ss7_link_m3ua:get_link_state(L#loop_dat.link)]), +            loop(L); +		Stop -> +            M3uaPid = L#loop_dat.m3ua_pid, +            %~ ScrcPid = L#loop_dat.scrc_pid, +            SS7linksPid = L#loop_dat.ss7links_pid, +            %~ SS7routesPid = L#loop_dat.ss7routes_pid, +			io:format("Received ~p~n", [Stop]), +            sccp_user:stop(), +            sccp_scrc:stop(), +            %~ ss7_link_m3ua:stop(), +            gen_server:cast(M3uaPid, stop), +            ss7_routes:stop(), +            %~ ss7_links:stop(), +            gen_server:cast(SS7linksPid, stop), +			exit(stop_received) +	end. + +test_hlr(L) -> +    LocalSsn = ?SCCP_SSN_MSC, +    ok = sccp_user:bind_ssn(LocalSsn), +    Gts = {L#loop_dat.gt_local, L#loop_dat.gt_hlr}, +    test_sri(Gts, L), +    test_srifs(Gts, L), +    L2 = test_si(Gts, L), +    test_sai(Gts, L2, 100), +    test_sai(Gts, L2, 10), +    test_sai(Gts, L2, 5), +    test_rss(Gts, L2), +    test_ess(Gts, L2), +    test_ul(Gts, L2), +    test_ati(Gts, L2), +    test_pms(Gts, L2), +    ok = sccp_user:unbind_ssn(LocalSsn, undefined). + +test_sri(Gts, L) -> +    %~ ======== +    %~ sendRoutingInfo +    %~ ======== +    io:format("~n\e[93;1m# Testing sendRoutingInfo...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_sendRoutingInfo(L#loop_dat.msisdn, L#loop_dat.gt_local)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +              {ok, Results} -> +                io:format("\e[97;1mGot answer for sendRoutingInfo~n~w~n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]); +                  [{basicROS, +                    {returnResult, +                      {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult', +                        {present,1},{'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult_result', +                          {local,22},_}}}}|_] -> +                    io:format("\e[91;1mReceived SendRoutingInfoRes~n\e[39;49;0m"); +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding SendRoutingInfo\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for SendRoutingInfo\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving SendRoutingInfo\n\e[39;49;0m") +    end, +    L. + +test_srifs(Gts, L) -> +    %~ ======== +    %~ sendRoutingInfoForSM +    %~ ======== +    io:format("~n\e[93;1m# Testing sendRoutingInfoForSM...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_sendRoutingInfoForSM(L#loop_dat.msisdn, L#loop_dat.scenter)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +              {ok, Results} -> +                io:format("\e[97;1mGot answer for sendRoutingInfoForSM\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}|_] -> +                    case {Present, Local} of +                      {1, 6} -> +                        io:format("\e[91;1mSubscriber is absent~n\e[39;49;0m"); +                      _ -> +                        io:format("\e[92;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]) +                    end; +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding SendRoutingInfoForSM\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for SendRoutingInfoForSM\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving SendRoutingInfoForSM\n\e[39;49;0m") +    end, +    L. + +test_si(Gts, L) -> +    %~ ======== +    %~ sendImsi +    %~ ======== +    io:format("~n\e[93;1m# Testing sendImsi...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_sendImsi(L#loop_dat.msisdn)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +              {ok, Results} -> +                io:format("\e[97;1mGot answer for sendImsi\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]), +                    L; +                  [{basicROS, +                    {returnResult, +                      {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult', +                        {present,1}, +                          {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult_result', +                            {local,58}, +                              Imsi }}}}|_] -> +                    io:format("\e[91;1mReceived IMSI ~w~n\e[39;49;0m", [Imsi]), +                    L#loop_dat{imsi = Imsi} +                end; +              _-> +                io:format("\e[91;1mError decoding sendImsi\n\e[39;49;0m"), +                L +            end; +        _-> +            io:format("\e[91;1mError no data received for sendImsi\n\e[39;49;0m"), +            L +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving sendImsi\n\e[39;49;0m"), +        L +    end. + +test_sai(Gts, L, Nr) -> +    %~ ======== +    %~ sendAuthenticationInfo +    %~ ======== +    io:format("~n\e[93;1m# Testing sendAuthenticationInfo...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_sendAuthenticationInfo(L#loop_dat.imsi, Nr)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +                {ok, Results} -> +                    io:format("\e[97;1mGot answer for sendAuthenticationInfo\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]); +                  [{basicROS, {reject, _}}|_] -> +                    if +                      Nr>5 -> io:format("\e[92;1mAsked for ~w (>5) vectors, got rejected~n\e[39;49;0m", [Nr]); +                      Nr<6 -> io:format("\e[91;1mAsked for ~w (<=5) vectors, got rejected~n\e[39;49;0m", [Nr]) +                    end; +                  [{basicROS, +                    {returnResult, +                      {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult', +                        {present,1}, {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult_result', +                          {local,56}, {'SendAuthenticationInfoRes', +                            {quintupletList,ResList},_,_}}}}}|_] -> +                    NrRes = length(ResList), +                    if +                      Nr>5 -> +                        if +                          NrRes>5 -> io:format("\e[91;1mAsked for ~w vectors, got ~w (>5) result vectors~n\e[39;49;0m", [Nr,NrRes]) +                        end; +                      Nr<6 -> +                        if +                          NrRes==Nr -> io:format("\e[92;1mAsked for ~w vectors, got ~w (=~w) result vectors~n\e[39;49;0m", [Nr,NrRes,Nr]); +                          true ->     io:format("\e[93;1mAsked for ~w vectors, got ~w (!=~w) result vectors~n\e[39;49;0m", [Nr,NrRes,Nr]) +                        end +                    end; +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding sendAuthenticationInfo\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for sendAuthenticationInfo\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving sendAuthenticationInfo\n\e[39;49;0m") +    end, +    L. + +test_rss(Gts, L) -> +    %~ ======== +    %~ registerSS +    %~ ======== +    io:format("~n\e[93;1m# Testing registerSS...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_registerSS(ss7test_helper:decode_imsi(L#loop_dat.imsi), L#loop_dat.gt_vlr, L#loop_dat.fnumber)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +                {ok, Results} -> +                    io:format("\e[97;1mGot answer for registerSS\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]); +                  [{basicROS, +                    {returnResult, +                      {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult', +                        {present,1}, {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult_result', +                          {local,10},_}}}}|_] -> +                    io:format("\e[91;1mReceived forwardingInfo, registerSS is working~n\e[39;49;0m"); +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding registerSS\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for registerSS\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving registerSS\n\e[39;49;0m") +    end, +    L. + +test_ess(Gts, L) -> +    %~ ======== +    %~ eraseSS +    %~ ======== +    io:format("~n\e[93;1m# Testing eraseSS...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_eraseSS(ss7test_helper:decode_imsi(L#loop_dat.imsi), L#loop_dat.gt_vlr)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +                {ok, Results} -> +                    io:format("\e[97;1mGot answer for eraseSS\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]); +                  [{basicROS, +                    {returnResult, +                      {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult', +                        {present,1}, {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult_result', +                          {local,11},_}}}}|_] -> +                    io:format("\e[91;1mReceived forwardingInfo, eraseSS is working~n\e[39;49;0m"); +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding eraseSS\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for eraseSS\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving eraseSS\n\e[39;49;0m") +    end, +    L. + +test_ul(Gts, L) -> +    %~ ======== +    %~ updateLocation +    %~ ======== +    io:format("~n\e[93;1m# Testing updateLocation...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_updateLocation(L#loop_dat.imsi, L#loop_dat.gt_local, L#loop_dat.gt_local)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +                {ok, Results} -> +                    io:format("\e[97;1mGot answer for updateLocation\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]); +                  [{basicROS,{invoke,{'MapSpecificPDUs_continue_components_SEQOF_basicROS_invoke',{present,2},asn1_NOVALUE,{local,7},_}}}|_] -> +                    io:format("\e[91;1mReceived insertSubscriberData, updateLocation is working~n\e[39;49;0m"); +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding updateLocation\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for updateLocation\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving updateLocation\n\e[39;49;0m") +    end, +    L. + +test_ati(Gts, L) -> +    %~ ======== +    %~ anyTimeInterrogation +    %~ ======== +    io:format("~n\e[93;1m# Testing anyTimeInterrogation...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_anyTimeInerrogation(L#loop_dat.imsi, L#loop_dat.gt_local)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +                {ok, Results} -> +                    io:format("\e[97;1mGot answer for anyTimeInerrogation\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    case {Present, Local} of +                      {1, 49} -> +                        io:format("\e[92;1manyTimeInterrogation is forbidden~n\e[39;49;0m"); +                      _ -> +                        io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]) +                    end; +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding anyTimeInerrogation\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for anyTimeInerrogation\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving anyTimeInerrogation\n\e[39;49;0m") +    end, +    L. + +test_pms(Gts, L) -> +    %~ ======== +    %~ purgeMS +    %~ ======== +    io:format("~n\e[93;1m# Testing purgeMS...\n\e[39;49;0m"), +    send_tcap(L, Gts, {?SCCP_SSN_MSC, ?SCCP_SSN_HLR}, map_msgs:create_purgeMs(L#loop_dat.imsi, L#loop_dat.gt_local)), +    receive +        {sccp, {primitive, 'N', 'UNITDATA', indication, Data}} -> +            case decode_tcap(Data) of +                {ok, Results} -> +                    io:format("\e[97;1mGot answer for purgeMs\n~w\n\e[39;49;0m", [Results]), +                case Results of +                  [{basicROS, {returnError, {_, {present, Present}, {local, Local}, _}}}] -> +                    io:format("\e[91;1mReceived Error: Present ~w, Local ~w~n\e[39;49;0m", [Present, Local]); +                  [{basicROS, +                    {returnResult, +                      {'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult', +                        {present,1},{'MapSpecificPDUs_end_components_SEQOF_basicROS_returnResult_result', +                          {local,67},_}}}}|_] -> +                    io:format("\e[91;1mReceived purgeMS-Res, purgeMS is working~n\e[39;49;0m"); +                  _ -> +                    io:format("\e[92;1mNo Error.~n\e[39;49;0m") +                end; +              _-> +                io:format("\e[91;1mError decoding purgeMs\n\e[39;49;0m") +            end; +        _-> +            io:format("\e[91;1mError no data received for purgeMs\n\e[39;49;0m") +    after 2000 -> +        io:format("\e[91;1mError timeout on receiving purgeMs\n\e[39;49;0m") +    end, +    L. + +scrc_tx_to_mtp(Prim, Args) -> +	M3uaPid = Args, +	gen_fsm:send_event(M3uaPid, Prim). + +send_tcap(L, {Lgt, Rgt}, {Sssn, Dssn}, PDU) -> +    CallingP = #sccp_addr{ssn = Sssn, point_code = L#loop_dat.local_pc,  +        global_title = #global_title{gti = ?SCCP_GTI_TT_NP_ENC_NAT, trans_type = ?SCCP_GTI_NO_GT, +            encoding = 0, numbering_plan = 1, phone_number = Lgt, +            nature_of_addr_ind = ?ISUP_ADDR_NAT_INTERNATIONAL}}, +	CalledP = #sccp_addr{ssn = Dssn, point_code = L#loop_dat.remote_pc, +        global_title = #global_title{gti = ?SCCP_GTI_TT_NP_ENC_NAT, trans_type = ?SCCP_GTI_NO_GT, +            encoding = 0, numbering_plan = 1, phone_number = Rgt, +            nature_of_addr_ind = ?ISUP_ADDR_NAT_INTERNATIONAL}}, +	Opts = [{protocol_class, {1, 8}}, {called_party_addr, CalledP}, +		{calling_party_addr, CallingP}, {user_data, PDU}], +    gen_fsm:send_event(L#loop_dat.scrc_pid, osmo_util:make_prim('N','UNITDATA',request,Opts)). + +decode_tcap(Data) -> +    {sccp_msg, _, ProtData} = Data, +    {user_data, UserData} = lists:keyfind(user_data, 1, ProtData), +    {ok, TcapData} = map:decode('MapSpecificPDUs', UserData), +    case TcapData of +      {'end', {'MapSpecificPDUs_end', _Transaction, % <<1,1,0,0>>, +        {'EXTERNAL', +          {0,0,17,773,1,1,1}, +          _,_, %asn1_NOVALUE,asn1_NOVALUE, +          _Dialog}, Results}} -> {}; +      {continue, {'MapSpecificPDUs_continue', _STransaction, _Transaction, +        {'EXTERNAL', +          {0,0,17,773,1,1,1}, +          _,_, %asn1_NOVALUE,asn1_NOVALUE, +          _Dialog}, Results}} -> {} +    end, +    {ok, Results}. diff --git a/src/ss7test_app.hrl b/src/ss7test_app.hrl new file mode 100644 index 0000000..49703c0 --- /dev/null +++ b/src/ss7test_app.hrl @@ -0,0 +1,6 @@ +% Number encoding Definitions + +-define(NUMBER_EXTENSION_NONE, 1). +-define(NUMBER_NATURE_INTERNATIONAL, 1). +-define(NUMBER_PLAN_ISDN, 1). +-define(NUMBER_LAND_MOBILE, 6). diff --git a/src/ss7test_helper.erl b/src/ss7test_helper.erl new file mode 100644 index 0000000..f6821b0 --- /dev/null +++ b/src/ss7test_helper.erl @@ -0,0 +1,48 @@ +-module(ss7test_helper). +-author('Daniel Mende <mail@c0decafe.de>'). + +-export([encode_phonenumber/4, encode_msisdn/4, +         decode_imsi/1, +         remove_firstN/2, +         tup2bin/1]). + +decode_imsi(<<>>) -> +    []; +decode_imsi(Imsi) -> +    <<First:4,Second:4,Rest/bits>> = Imsi, +    Dec = decode_imsi(Rest), +    if +      First==15 -> lists:append([Second], Dec); +      true ->       lists:append([Second,First], Dec) +    end. + +%% =================================================================== +%% Phone Number helper +%% =================================================================== + +encode_phonenumber(Extension, NatureOfNumber, NumberPlan, Number) -> +    {EncNumber, Digits} = encode_phonenumber(Number), +    <<Digits:8, Extension:1, NatureOfNumber:3, NumberPlan:4, EncNumber/binary>>. +encode_phonenumber([First,Second|Tail]) -> +    {EncNumber, Digits} = encode_phonenumber(Tail), +    {<<Second:4, First:4, EncNumber/binary>>, Digits + 2}; +encode_phonenumber([Last]) -> +    {<<15:4, Last:4>>, 1}; +encode_phonenumber([]) -> +    {<<>>, 0}. + +encode_msisdn(Extension, NatureOfNumber, NumberPlan, Number) -> +    {EncNumber, _} = encode_phonenumber(Number), +    <<Extension:1, NatureOfNumber:3, NumberPlan:4, EncNumber/binary>>. + +%% =================================================================== +%% List helper +%% =================================================================== + +remove_firstN(_, []) -> []; +remove_firstN(1, [_|T]) -> T; +remove_firstN(N, [_|T]) -> remove_firstN(N-1, T). + + +tup2bin(Tupel) -> +    binary:list_to_bin([element(I,Tupel) || I <- lists:seq(1,tuple_size(Tupel))]). diff --git a/src/ss7test_sup.erl b/src/ss7test_sup.erl new file mode 100644 index 0000000..1b47715 --- /dev/null +++ b/src/ss7test_sup.erl @@ -0,0 +1,27 @@ +-module(ss7test_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +%% Helper macro for declaring children of supervisor +-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). + +%% =================================================================== +%% API functions +%% =================================================================== + +start_link() -> +    supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% =================================================================== +%% Supervisor callbacks +%% =================================================================== + +init([]) -> +    {ok, { {one_for_one, 5, 10}, []} }. + diff --git a/ss7test.iml b/ss7test.iml new file mode 100644 index 0000000..7626f17 --- /dev/null +++ b/ss7test.iml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="ERLANG_MODULE" version="4"> +  <component name="FacetManager"> +    <facet type="erlang" name="Erlang"> +      <configuration /> +    </facet> +  </component> +  <component name="NewModuleRootManager" inherit-compiler-output="false"> +    <output url="file://$MODULE_DIR$/ebin" /> +    <output-test url="file://$MODULE_DIR$/.eunit" /> +    <exclude-output /> +    <content url="file://$MODULE_DIR$"> +      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> +    </content> +    <orderEntry type="inheritedJdk" /> +    <orderEntry type="sourceFolder" forTests="false" /> +    <orderEntry type="module" module-name="osmo_ss7" /> +    <orderEntry type="module" module-name="osmo_map" /> +  </component> +</module>
\ No newline at end of file | 
