blob: cdca67538d53a9f35ff0869de8110f894ad8575c [file] [log] [blame]
Harald Weltefa8ada02012-01-16 15:59:45 +01001% MTP3 Signalling Link Test Control (SLTC) according to Q.707
2
3% (C) 2011-2012 by Harald Welte <laforge@gnumonks.org>
4%
5% All Rights Reserved
6%
7% This program is free software; you can redistribute it and/or modify
8% it under the terms of the GNU Affero General Public License as
9% published by the Free Software Foundation; either version 3 of the
10% License, or (at your option) any later version.
11%
12% This program is distributed in the hope that it will be useful,
13% but WITHOUT ANY WARRANTY; without even the implied warranty of
14% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15% GNU General Public License for more details.
16%
17% You should have received a copy of the GNU Affero General Public License
18% along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20-module(mtp3_sltc).
21-author('Harald Welte <laforge@gnumonks.org>').
22-behaviour(gen_fsm).
23
24-include("mtp3.hrl").
25
26% gen_fsm exports
27-export([init/1, terminate/3, code_change/4, handle_event/3, handle_info/3]).
28
29% individual FSM states
30-export([idle/2, first_attempt/2, second_attempt/2]).
31
32-record(sltc_state, {
33 hmrt_pid,
34 mgmt_pid,
35 lsac_pid,
36 sls,
37 opc,
38 adj_dpc,
39 t1,
40 t1_timeout,
41 x
42 }).
43
44-define(SLTC_T1_DEF, 10000).
45-define(SLTC_T2_DEF, 60000).
46
47%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48% gen_fsm callbacks
49%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50
51init([Hmrt, Mgmt, Lsac, Sls, AdjDpc, Opc]) when
52 is_pid(Hmrt), is_pid(Mgmt), is_pid(Lsac), is_integer(Sls) ->
53 SltState = #sltc_state{hmrt_pid = Hmrt,
54 mgmt_pid = Mgmt,
55 lsac_pid = Lsac,
56 sls = Sls,
57 adj_dpc = AdjDpc,
58 opc = Opc,
59 t1_timeout = ?SLTC_T1_DEF,
60 x = 16#2342},
61 {ok, idle, SltState}.
62
63terminate(Reason, State, _LoopDat) ->
64 io:format("Terminating ~p in State ~p (Reason: ~p)~n",
65 [?MODULE, State, Reason]),
66 ok.
67
68code_change(_OldVsn, StateName, LoopDat, _Extra) ->
69 {ok, StateName, LoopDat}.
70
71handle_event(Event, State, LoopDat) ->
72 io:format("Unknown Event ~p in state ~p~n", [Event, State]),
73 {next_state, State, LoopDat}.
74
75handle_info(Info, State, LoopDat) ->
76 io:format("Unknown Info ~p in state ~p~n", [Info, State]),
77 {next_state, State, LoopDat}.
78
79% See Figure 2 of Q.707
80
81%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82% STATE: idle
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84
Harald Welte26bdef22012-01-16 22:22:17 +010085idle(M=#mtp3_msg{service_ind = ?MTP3_SERV_MTN,
Harald Weltefa8ada02012-01-16 15:59:45 +010086 payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST,
87 h1 = ?MTP3MG_H1_SLTM}}, LoopDat) ->
88 Slta = slta_from_sltm(M),
89 send_to(hmrt, Slta, LoopDat),
90 {next_state, idle, LoopDat};
91
92idle(start, LoopDat) ->
93 Sltm = generate_sltm(LoopDat),
94 send_to(hmrt, Sltm, LoopDat),
95 {ok, T1} = timer:apply_after(gen_fsm, send_event,
96 [self(), {timer_expired, t1}]),
97 {next_state, first_attempt, LoopDat#sltc_state{t1 = T1}}.
98
99
100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101% STATE: first_attempt
102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103
Harald Welte26bdef22012-01-16 22:22:17 +0100104first_attempt(M=#mtp3_msg{service_ind = ?MTP3_SERV_MTN,
Harald Weltefa8ada02012-01-16 15:59:45 +0100105 payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST,
106 h1 = ?MTP3MG_H1_SLTM}}, LoopDat) ->
107 Slta = slta_from_sltm(M),
108 send_to(hmrt, Slta, LoopDat),
109 {next_state, first_attempt, LoopDat};
110
Harald Welte26bdef22012-01-16 22:22:17 +0100111first_attempt(M = #mtp3_msg{service_ind = ?MTP3_SERV_MTN,
Harald Weltefa8ada02012-01-16 15:59:45 +0100112 payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST,
113 h1 = ?MTP3MG_H1_SLTA}}, LoopDat) ->
114 timer:cancel(LoopDat#sltc_state.t1),
115 case slt_matches(M, LoopDat) of
116 true ->
117 send_to(lsac, slt_successful, LoopDat),
118 {next_state, idle, LoopDat};
119 false ->
120 Sltm = generate_sltm(LoopDat),
121 send_to(hmrt, Sltm, LoopDat),
122 {ok, T1} = timer:apply_after(gen_fsm, send_event,
123 [self(), {timer_expired, t1}]),
124 {next_state, second_attempt, LoopDat#sltc_state{t1 = T1}}
125 end.
126
127
128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129% STATE: second_attempt
130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131
Harald Welte26bdef22012-01-16 22:22:17 +0100132second_attempt(M=#mtp3_msg{service_ind = ?MTP3_SERV_MTN,
Harald Weltefa8ada02012-01-16 15:59:45 +0100133 payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST,
134 h1 = ?MTP3MG_H1_SLTM}}, LoopDat) ->
135 Slta = slta_from_sltm(M),
136 send_to(hmrt, Slta, LoopDat),
137 {next_state, second_attempt, LoopDat};
138
Harald Welte26bdef22012-01-16 22:22:17 +0100139second_attempt(M = #mtp3_msg{service_ind = ?MTP3_SERV_MTN,
Harald Weltefa8ada02012-01-16 15:59:45 +0100140 payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST,
141 h1 = ?MTP3MG_H1_SLTA}}, LoopDat) ->
142 timer:cancel(LoopDat#sltc_state.t1),
143 case slt_matches(M, LoopDat) of
144 true ->
145 send_to(lsac, slt_successful, LoopDat);
146 false ->
147 send_to(mgmt, slt_failed, LoopDat),
148 send_to(lsac, slt_failed, LoopDat)
149 end,
150 {next_state, idle, LoopDat};
151
152second_attempt({timer_expired, t1}, LoopDat) ->
153 send_to(mgmt, slt_failed, LoopDat),
154 send_to(lsac, slt_failed, LoopDat),
155 {next_state, idle, LoopDat}.
156
157
158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159% helper functions
160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
161send_to(hmrt, What, #sltc_state{hmrt_pid = Txc}) ->
162 Txc ! {sltc_hmrt, What};
163send_to(mgmt, What, #sltc_state{mgmt_pid = Txc}) ->
164 Txc ! {sltc_mgmt, What};
165send_to(lsac, What, #sltc_state{lsac_pid = Txc}) ->
166 Txc ! {sltc_lsac, What}.
167
Harald Welte26bdef22012-01-16 22:22:17 +0100168slta_from_sltm(M = #mtp3_msg{service_ind = ?MTP3_SERV_MTN,
Harald Weltefa8ada02012-01-16 15:59:45 +0100169 routing_label = RoutLbl,
170 payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST,
171 h1 = ?MTP3MG_H1_SLTM,
Harald Welte26bdef22012-01-16 22:22:17 +0100172 payload = TP}}) ->
Harald Welte1180b7c2012-01-25 01:28:56 +0100173 InvRoutLbl = mtp3_codec:invert_rout_lbl(RoutLbl),
Harald Weltefa8ada02012-01-16 15:59:45 +0100174 M#mtp3_msg{routing_label = InvRoutLbl,
175 payload = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST,
176 h1 = ?MTP3MG_H1_SLTA,
Harald Welte26bdef22012-01-16 22:22:17 +0100177 payload = TP}}.
Harald Weltefa8ada02012-01-16 15:59:45 +0100178
179generate_sltm(LoopDat) ->
180 Mg = #mtp3mg_msg{h0 = ?MTP3MG_H0_TEST, h1 = ?MTP3MG_H1_SLTM,
Harald Welte26bdef22012-01-16 22:22:17 +0100181 payload = LoopDat#sltc_state.x},
Harald Weltefa8ada02012-01-16 15:59:45 +0100182 Lbl = #mtp3_routing_label{sig_link_sel = LoopDat#sltc_state.sls,
183 origin_pc = LoopDat#sltc_state.opc,
184 dest_pc = LoopDat#sltc_state.adj_dpc},
185
186 #mtp3_msg{network_ind = ?MTP3_NETIND_INTERNATIONAL,
Harald Welte26bdef22012-01-16 22:22:17 +0100187 service_ind = ?MTP3_SERV_MTN,
Harald Weltefa8ada02012-01-16 15:59:45 +0100188 routing_label = Lbl, payload = Mg}.
189
190rout_lbl_matches(#mtp3_routing_label{sig_link_sel = SlsLocal,
191 origin_pc = OPC}, LoopDat) ->
192 #sltc_state{adj_dpc = AdjDpc, sls = SLS} = LoopDat,
193 if SLS == SlsLocal, OPC == AdjDpc ->
194 true;
195 true ->
196 false
197 end.
198
199slt_matches(#mtp3_msg{routing_label = RoutLbl,
Harald Welte26bdef22012-01-16 22:22:17 +0100200 payload = #mtp3mg_msg{payload = TP}}, LoopDat) ->
Harald Weltefa8ada02012-01-16 15:59:45 +0100201 case LoopDat#sltc_state.x of
202 TP ->
203 rout_lbl_matches(RoutLbl, LoopDat);
204 _ ->
205 false
206 end.