diff options
Diffstat (limited to 'patches')
| -rw-r--r-- | patches/osmo_map.patch | 64 | ||||
| -rw-r--r-- | patches/osmo_sccp.patch | 187 | ||||
| -rw-r--r-- | patches/osmo_ss7.patch | 437 | 
3 files changed, 688 insertions, 0 deletions
| 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, | 
