blob: 5a80e5d954ad6f88ef81de135c270fe8176c7a7e [file] [log] [blame]
Harald Welte033cef02010-12-19 22:47:14 +01001% ITU-T Q.71x SCCP Message coding / decoding
2
3% (C) 2010 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(sccp_codec).
21-author('Harald Welte <laforge@gnumonks.org>').
22-include("sccp.hrl").
23
Harald Welte9abbbad2011-04-21 12:19:41 +020024-export([parse_sccp_msg/1, encode_sccp_msg/1, encode_sccp_msgt/2,
25 is_connectionless/1]).
Harald Welte033cef02010-12-19 22:47:14 +010026
Harald Welte9baab6d2011-12-08 00:46:00 +010027-export([gen_gt_helper/1, gen_addr_helper/2, gen_addr_helper/3]).
28
Harald Welte033cef02010-12-19 22:47:14 +010029-compile(export_all).
30
Harald Welte2edaf552011-04-02 16:46:16 +020031-compile({parse_transform, exprecs}).
Harald Welte0f2f5962011-04-04 15:59:49 +020032-export_records([global_title, sccp_addr, sccp_msg]).
Harald Welte2edaf552011-04-02 16:46:16 +020033
Harald Welted9c318f2011-12-10 22:17:11 +010034binarify(In) when is_binary(In) ->
35 In;
36binarify(In) when is_list(In) ->
37 list_to_binary(In).
38
Harald Welte2b4b2672011-02-03 12:50:41 +010039parse_point_code(BinPC, PCind) when is_binary(BinPC) ->
Harald Welteba6fdbb2011-01-23 22:04:39 +010040 case PCind of
41 1 ->
Harald Welte9c3b1bb2011-10-12 17:00:34 +020042 <<PointCode:16/little, Remain/binary>> = BinPC;
Harald Welteba6fdbb2011-01-23 22:04:39 +010043 _ ->
44 Remain = BinPC,
Harald Welteb5936ba2011-12-08 00:58:51 +010045 PointCode = undefined
Harald Welteba6fdbb2011-01-23 22:04:39 +010046 end,
Harald Welte2b4b2672011-02-03 12:50:41 +010047 {Remain, PointCode}.
Harald Welteba6fdbb2011-01-23 22:04:39 +010048
Harald Welte2b4b2672011-02-03 12:50:41 +010049parse_ssn(BinSSN, SSNind) ->
Harald Welteba6fdbb2011-01-23 22:04:39 +010050 case SSNind of
51 1 ->
Harald Welte2b4b2672011-02-03 12:50:41 +010052 <<SSN:8, Remain/binary>> = BinSSN;
Harald Welteba6fdbb2011-01-23 22:04:39 +010053 _ ->
54 Remain = BinSSN,
Harald Welteb5936ba2011-12-08 00:58:51 +010055 SSN = undefined
Harald Welteba6fdbb2011-01-23 22:04:39 +010056 end,
Harald Welte2b4b2672011-02-03 12:50:41 +010057 {Remain, SSN}.
Harald Welteba6fdbb2011-01-23 22:04:39 +010058
59enc_is_odd(Enc) ->
60 case Enc of
61 1 -> 1;
62 _ -> 0
63 end.
64
Harald Welte2b4b2672011-02-03 12:50:41 +010065parse_gt(BinGT, GTind) ->
Harald Welteba6fdbb2011-01-23 22:04:39 +010066 case GTind of
67 ?SCCP_GTI_NO_GT ->
Harald Welteb5936ba2011-12-08 00:58:51 +010068 undefined;
Harald Welteba6fdbb2011-01-23 22:04:39 +010069 ?SCCP_GTI_NAT_ONLY ->
70 % Figure 7/Q.713
71 <<Odd:1, Nature:7, Digits/binary>> = BinGT,
72 PhoneNum = isup_codec:parse_isup_party(Digits, Odd),
Harald Welte2b4b2672011-02-03 12:50:41 +010073 #global_title{gti = GTind,
74 nature_of_addr_ind = Nature,
75 phone_number = PhoneNum};
Harald Welteba6fdbb2011-01-23 22:04:39 +010076 ?SCCP_GTI_TT_ONLY ->
77 % Figure 9/Q.913
78 <<TransType:8, Digits/binary>> = BinGT,
79 % Used in national interfaces only, we cannot parse Digits
Harald Welte2b4b2672011-02-03 12:50:41 +010080 #global_title{gti = GTind,
81 trans_type = TransType,
82 phone_number = Digits};
Harald Welteba6fdbb2011-01-23 22:04:39 +010083 ?SCCP_GTI_TT_NP_ENC ->
84 % Figure 10/Q.713
85 <<TransType:8, NumPlan:4, Enc:4, Digits/binary>> = BinGT,
86 PhoneNum = isup_codec:parse_isup_party(Digits, enc_is_odd(Enc)),
Harald Welte2b4b2672011-02-03 12:50:41 +010087 #global_title{gti = GTind,
Harald Welte2c67ac02012-01-18 08:49:45 +010088 trans_type = TransType,
Harald Welte2b4b2672011-02-03 12:50:41 +010089 numbering_plan = NumPlan,
90 phone_number = PhoneNum};
Harald Welteba6fdbb2011-01-23 22:04:39 +010091 ?SCCP_GTI_TT_NP_ENC_NAT ->
92 % Figure 11/Q.713
93 <<TransType:8, NumPlan:4, Enc:4, 0:1, Nature:7, Digits/binary>> = BinGT,
94 PhoneNum = isup_codec:parse_isup_party(Digits, enc_is_odd(Enc)),
Harald Welte2b4b2672011-02-03 12:50:41 +010095 #global_title{gti = GTind,
Harald Welte2c67ac02012-01-18 08:49:45 +010096 trans_type = TransType,
Harald Welte2b4b2672011-02-03 12:50:41 +010097 numbering_plan = NumPlan,
98 nature_of_addr_ind = Nature,
99 phone_number = PhoneNum};
Harald Welteba6fdbb2011-01-23 22:04:39 +0100100 _ ->
Harald Welte2b4b2672011-02-03 12:50:41 +0100101 BinGT
102 end.
Harald Welteba6fdbb2011-01-23 22:04:39 +0100103
104% parse SCCP Address
105parse_sccp_addr(BinAddr) when is_binary(BinAddr) ->
106 <<ResNatUse:1, RoutInd:1, GTind:4, SSNind:1, PCind:1, Remain/binary>> = BinAddr,
Harald Welte2b4b2672011-02-03 12:50:41 +0100107 {RemainPC, OptPC} = parse_point_code(Remain, PCind),
108 {RemainSSN, OptSSN} = parse_ssn(RemainPC, SSNind),
109 OptGT = parse_gt(RemainSSN, GTind),
110 #sccp_addr{res_nat_use = ResNatUse, route_on_ssn = RoutInd,
111 point_code = OptPC, ssn = OptSSN, global_title = OptGT}.
Harald Welte033cef02010-12-19 22:47:14 +0100112
113% parse SCCP Optional Part
Harald Welte9dda4e12012-01-23 16:15:06 +0100114parse_sccp_opt(OptType, _OptLen, Content) ->
115 OptAtom = opt_to_atom(OptType),
116 {OptAtom, Content}.
Harald Welte033cef02010-12-19 22:47:14 +0100117
118parse_sccp_opts(<<>>, OptList) ->
119 % empty list
120 OptList;
121parse_sccp_opts(<<0>>, OptList) ->
122 % end of options
123 OptList;
124parse_sccp_opts(OptBin, OptList) ->
125 <<OptType, OptLen, Content:OptLen/binary, Remain/binary>> = OptBin,
126 NewOpt = parse_sccp_opt(OptType, OptLen, Content),
127 parse_sccp_opts(Remain, [NewOpt|OptList]).
128
Harald Welte9dda4e12012-01-23 16:15:06 +0100129
Harald Welte033cef02010-12-19 22:47:14 +0100130% Parse incoming SCCP message, one function for every message type
131parse_sccp_msgt(?SCCP_MSGT_CR, DataBin) ->
132 % first get the fixed part
Harald Welte11565772011-04-15 10:37:57 +0200133 <<_:8, SrcLocalRef:24/big, PCOpt:4, ProtoClass:4, RemainVar/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100134 % variable length fixed part
135 <<PtrVar:8, PtrOpt:8, _/binary>> = RemainVar,
136 CalledPartyLen = binary:at(RemainVar, PtrVar),
137 CalledParty = binary:part(RemainVar, PtrVar+1, CalledPartyLen),
Harald Welte234c9562011-02-03 13:51:12 +0100138 CalledPartyDec = parse_sccp_addr(CalledParty),
Harald Welte033cef02010-12-19 22:47:14 +0100139 % optional part
140 OptBin = binary:part(RemainVar, 1 + PtrOpt, byte_size(RemainVar)-(1+PtrOpt)),
141 OptList = parse_sccp_opts(OptBin, []),
142 %OptList = [],
143 % build parsed list of message
Harald Welte11565772011-04-15 10:37:57 +0200144 [{src_local_ref, SrcLocalRef},{protocol_class, {ProtoClass, PCOpt}},
145 {called_party_addr, CalledPartyDec} | OptList];
Harald Welte033cef02010-12-19 22:47:14 +0100146parse_sccp_msgt(?SCCP_MSGT_CC, DataBin) ->
147 % first get the fixed part
Harald Weltecbddf842012-01-24 22:58:01 +0100148 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, PCOpt:4, ProtoClass:4, PtrOpt:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100149 % optional part
Harald Weltecbddf842012-01-24 22:58:01 +0100150 OptBin = binary:part(Remain, PtrOpt-1, byte_size(Remain)-(PtrOpt-1)),
151 OptList = parse_sccp_opts(OptBin, []),
Harald Welte033cef02010-12-19 22:47:14 +0100152 % build parsed list of message
Harald Welte11565772011-04-15 10:37:57 +0200153 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},
154 {protocol_class, {ProtoClass, PCOpt}} | OptList];
Harald Welte033cef02010-12-19 22:47:14 +0100155parse_sccp_msgt(?SCCP_MSGT_CREF, DataBin) ->
156 % first get the fixed part
Harald Welte56ee7a62010-12-20 13:34:32 +0100157 <<_:8, DstLocalRef:24/big, RefusalCause:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100158 % optional part
159 OptList = parse_sccp_opts(Remain, []),
160 % build parsed list of message
161 [{dst_local_ref, DstLocalRef},{refusal_cause, RefusalCause}|OptList];
162parse_sccp_msgt(?SCCP_MSGT_RLSD, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100163 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ReleaseCause:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100164 % optional part
165 OptList = parse_sccp_opts(Remain, []),
166 % build parsed list of message
167 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{release_cause, ReleaseCause}|OptList];
168parse_sccp_msgt(?SCCP_MSGT_RLC, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100169 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100170 % build parsed list of message
171 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef}];
172parse_sccp_msgt(?SCCP_MSGT_DT1, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100173 <<_:8, DstLocalRef:24/big, SegmReass:8, DataPtr:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100174 DataLen = binary:at(Remain, DataPtr-1),
175 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
176 % build parsed list of message
177 [{dst_local_ref, DstLocalRef},{segm_reass, SegmReass},{user_data, UserData}];
178parse_sccp_msgt(?SCCP_MSGT_DT2, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100179 <<_:8, DstLocalRef:24/big, SeqSegm:16, DataPtr:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100180 DataLen = binary:at(Remain, DataPtr-1),
181 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
182 % build parsed list of message
183 [{dst_local_ref, DstLocalRef},{seq_segm, SeqSegm},{user_data, UserData}];
184parse_sccp_msgt(?SCCP_MSGT_AK, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100185 <<_:8, DstLocalRef:24/big, RxSeqnr:8, Credit:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100186 [{dst_local_ref, DstLocalRef},{rx_seq_nr, RxSeqnr},{credit, Credit}];
187parse_sccp_msgt(?SCCP_MSGT_UDT, DataBin) ->
Harald Welte11565772011-04-15 10:37:57 +0200188 <<_:8, PCOpt:4, ProtoClass:4, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100189 % variable part
190 CalledPartyLen = binary:at(Remain, CalledPartyPtr-3),
191 CalledParty = binary:part(Remain, CalledPartyPtr-3+1, CalledPartyLen),
Harald Welte234c9562011-02-03 13:51:12 +0100192 CalledPartyDec = parse_sccp_addr(CalledParty),
Harald Welte033cef02010-12-19 22:47:14 +0100193 CallingPartyLen = binary:at(Remain, CallingPartyPtr-2),
194 CallingParty = binary:part(Remain, CallingPartyPtr-2+1, CallingPartyLen),
Harald Welte234c9562011-02-03 13:51:12 +0100195 CallingPartyDec = parse_sccp_addr(CallingParty),
Harald Welte033cef02010-12-19 22:47:14 +0100196 DataLen = binary:at(Remain, DataPtr-1),
197 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
Harald Welte11565772011-04-15 10:37:57 +0200198 [{protocol_class, {ProtoClass, PCOpt}},{called_party_addr, CalledPartyDec},
Harald Welte234c9562011-02-03 13:51:12 +0100199 {calling_party_addr, CallingPartyDec},{user_data, UserData}];
Harald Welte033cef02010-12-19 22:47:14 +0100200parse_sccp_msgt(?SCCP_MSGT_UDTS, DataBin) ->
Harald Welte030f1092011-03-11 19:08:07 +0100201 <<_:8, ReturnCause:8, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary >> = DataBin,
202 % variable part
203 CalledPartyLen = binary:at(Remain, CalledPartyPtr-3),
204 CalledParty = binary:part(Remain, CalledPartyPtr-3+1, CalledPartyLen),
205 CalledPartyDec = parse_sccp_addr(CalledParty),
206 CallingPartyLen = binary:at(Remain, CallingPartyPtr-2),
207 CallingParty = binary:part(Remain, CallingPartyPtr-2+1, CallingPartyLen),
208 CallingPartyDec = parse_sccp_addr(CallingParty),
209 DataLen = binary:at(Remain, DataPtr-1),
210 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
211 [{return_cause, ReturnCause},{called_party_addr, CalledPartyDec},
212 {calling_party_addr, CallingPartyDec},{user_data, UserData}];
Harald Welte033cef02010-12-19 22:47:14 +0100213parse_sccp_msgt(?SCCP_MSGT_ED, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100214 <<_:8, DstLocalRef:24/big, DataPtr:8, Remain/binary>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100215 DataLen = binary:at(Remain, DataPtr-1),
216 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
217 [{dst_local_ref, DstLocalRef}, {user_data, UserData}];
218parse_sccp_msgt(?SCCP_MSGT_EA, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100219 <<_:8, DstLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100220 [{dst_local_ref, DstLocalRef}];
221parse_sccp_msgt(?SCCP_MSGT_RSR, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100222 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ResetCause:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100223 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{reset_cause, ResetCause}];
224parse_sccp_msgt(?SCCP_MSGT_RSC, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100225 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100226 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef}];
227parse_sccp_msgt(?SCCP_MSGT_ERR, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100228 <<_:8, DstLocalRef:24/big, ErrCause:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100229 [{dst_local_ref, DstLocalRef},{error_cause, ErrCause}];
230parse_sccp_msgt(?SCCP_MSGT_IT, DataBin) ->
Harald Welte11565772011-04-15 10:37:57 +0200231 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, PCOpt: 4, ProtoClass:4, SegmSeq:16, Credit:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100232 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},
Harald Welte11565772011-04-15 10:37:57 +0200233 {protocol_class, {ProtoClass, PCOpt}},{seq_segm, SegmSeq},{credit, Credit}].
Harald Welte033cef02010-12-19 22:47:14 +0100234% FIXME: XUDT/XUDTS, LUDT/LUDTS
235
236% process one incoming SCCP message
237parse_sccp_msg(DataBin) ->
238 MsgType = binary:first(DataBin),
239 Parsed = parse_sccp_msgt(MsgType, DataBin),
240 {ok, #sccp_msg{msg_type = MsgType, parameters = Parsed}}.
241
242% Encoding Part
243
Harald Welte0e1709c2011-02-06 22:17:53 +0100244gt_enc_by_odd(Odd) ->
245 if Odd == 1 ->
246 1;
247 true ->
248 2
249 end.
250
Harald Welte5a1cf3c2011-04-14 21:55:13 +0200251encode_gt(undefined) ->
252 {?SCCP_GTI_NO_GT, <<>>};
Harald Welte234c9562011-02-03 13:51:12 +0100253encode_gt(#global_title{gti = GTind, phone_number = PhoneNum,
254 nature_of_addr_ind = Nature,
Harald Welte2c67ac02012-01-18 08:49:45 +0100255 trans_type = TransType,
Harald Welte234c9562011-02-03 13:51:12 +0100256 numbering_plan = NumPlan}) ->
257 case GTind of
258 ?SCCP_GTI_NO_GT ->
259 {GTind, <<>>};
260 ?SCCP_GTI_NAT_ONLY ->
261 % Figure 7/Q.713
262 {PhoneBin, OddEven} = isup_codec:encode_isup_party(PhoneNum),
263 {GTind, <<OddEven:1, Nature:7, PhoneBin/binary>>};
264 ?SCCP_GTI_TT_ONLY ->
265 % Figure 9/Q.913
266 % Used in national interfaces only, we cannot parse Digits
267 {GTind, <<TransType:8, PhoneNum/binary>>};
268 ?SCCP_GTI_TT_NP_ENC ->
269 % Figure 10/Q.713
Harald Welte0e1709c2011-02-06 22:17:53 +0100270 {PhoneBin, OddEven} = isup_codec:encode_isup_party(PhoneNum),
271 Enc = gt_enc_by_odd(OddEven),
Harald Welte234c9562011-02-03 13:51:12 +0100272 {GTind, <<TransType:8, NumPlan:4, Enc:4, PhoneBin/binary>>};
273 ?SCCP_GTI_TT_NP_ENC_NAT ->
274 % Figure 11/Q.713
Harald Welte0e1709c2011-02-06 22:17:53 +0100275 {PhoneBin, OddEven} = isup_codec:encode_isup_party(PhoneNum),
276 Enc = gt_enc_by_odd(OddEven),
Harald Welte234c9562011-02-03 13:51:12 +0100277 {GTind, <<TransType:8, NumPlan:4, Enc:4, 0:1, Nature:7, PhoneBin/binary>>}
278 end.
279
Harald Weltec923a2a2012-01-23 14:13:05 +0100280encode_pc(undefined) ->
281 {0, <<>>};
Harald Welte683ed232011-12-08 00:56:54 +0100282encode_pc(PointCode) when is_integer(PointCode) ->
Harald Weltec923a2a2012-01-23 14:13:05 +0100283 {1, <<PointCode:16/little>>};
Harald Welte683ed232011-12-08 00:56:54 +0100284encode_pc(PcRec) ->
285 PcInt = osmo_util:pointcode2int(PcRec),
286 encode_pc(PcInt).
Harald Welte234c9562011-02-03 13:51:12 +0100287
288encode_ssn(SSN) ->
289 case SSN of
Harald Welteb5936ba2011-12-08 00:58:51 +0100290 undefined ->
Harald Welte234c9562011-02-03 13:51:12 +0100291 {0, <<>>};
292 _ ->
293 {1, <<SSN:8>>}
294 end.
295
Harald Welte5a1cf3c2011-04-14 21:55:13 +0200296undef_or_true(Foo) ->
297 case Foo of
298 undefined -> 0;
299 0 -> 0;
300 _ -> 1
301 end.
302
303
Harald Welte234c9562011-02-03 13:51:12 +0100304encode_sccp_addr(#sccp_addr{res_nat_use = ResNatUse,
305 route_on_ssn = RoutInd,
306 point_code = PointCode,
307 ssn = SSN,
308 global_title = GT}) ->
309
310 {GTind, GTbin} = encode_gt(GT),
311 {SSNind, SSNbin} = encode_ssn(SSN),
312 {PCind, PCbin} = encode_pc(PointCode),
Harald Welte5a1cf3c2011-04-14 21:55:13 +0200313 ResNatOut = undef_or_true(ResNatUse),
314 RoutIndOut = undef_or_true(RoutInd),
315 <<ResNatOut:1, RoutIndOut:1, GTind:4, SSNind:1, PCind:1, PCbin/binary, SSNbin/binary, GTbin/binary>>.
Harald Welte234c9562011-02-03 13:51:12 +0100316
317
Harald Welte9dda4e12012-01-23 16:15:06 +0100318encode_sccp_opt({AddrTag, AddrVal}) when AddrTag == ?SCCP_PNC_CALLED_PARTY_ADDRESS;
319 AddrTag == ?SCCP_PNC_CALLING_PARTY_ADDRESS ->
320 AddrEnc = encode_sccp_addr(AddrVal),
321 AddrLen = byte_size(AddrEnc),
322 <<AddrTag:8, AddrLen:8, AddrEnc/binary>>;
323encode_sccp_opt({OptInt, DataBin}) when is_binary(DataBin), is_integer(OptInt) ->
324 DataBinLen = byte_size(DataBin),
325 <<OptInt:8, DataBinLen:8, DataBin/binary>>;
326encode_sccp_opt({Opt, DataBin}) when is_atom(Opt) ->
327 OptNum = atom_to_opt(Opt),
328 encode_sccp_opt({OptNum, DataBin});
329encode_sccp_opt({Opt, DataInt}) when is_integer(DataInt), DataInt =< 255 ->
330 encode_sccp_opt({Opt, <<DataInt:8>>});
331encode_sccp_opt({Opt, DataList}) when is_list(DataList) ->
332 encode_sccp_opt({Opt, list_to_binary(DataList)}).
Harald Welte033cef02010-12-19 22:47:14 +0100333
Harald Welte9dda4e12012-01-23 16:15:06 +0100334encode_sccp_opts(OptList, Filter) ->
335 FilteredList = lists:filter(fun({Tag, _Val}) -> proplists:is_defined(opt_to_atom(Tag), Filter) end, OptList),
336 e_sccp_opts(FilteredList, []).
337
338e_sccp_opts([], OptEnc) ->
Harald Welte033cef02010-12-19 22:47:14 +0100339 % end of options + convert to binary
340 list_to_binary([OptEnc, ?SCCP_PNC_END_OF_OPTIONAL]);
Harald Welte9dda4e12012-01-23 16:15:06 +0100341e_sccp_opts([CurOpt|OptPropList], OptEnc) ->
Harald Welte033cef02010-12-19 22:47:14 +0100342 CurOptEnc = encode_sccp_opt(CurOpt),
Harald Welte9dda4e12012-01-23 16:15:06 +0100343 e_sccp_opts(OptPropList, list_to_binary([OptEnc,CurOptEnc])).
Harald Welte033cef02010-12-19 22:47:14 +0100344
Harald Welte033cef02010-12-19 22:47:14 +0100345
346encode_sccp_msgt(?SCCP_MSGT_CR, Params) ->
347 SrcLocalRef = proplists:get_value(src_local_ref, Params),
Harald Welte11565772011-04-15 10:37:57 +0200348 {ProtoClass, PCOpt} = proplists:get_value(protocol_class, Params),
Harald Welte9dda4e12012-01-23 16:15:06 +0100349 CalledParty = proplists:get_value(called_party_addr, Params),
350 CalledPartyEnc = encode_sccp_addr(CalledParty),
351 CalledPartyLen = byte_size(CalledPartyEnc),
352 PtrOpt = CalledPartyLen+1+1,
353 OptBin = encode_sccp_opts(Params, [credit, calling_party_addr, user_data, hop_counter, importance]),
354 <<?SCCP_MSGT_CR:8, SrcLocalRef:24/big, PCOpt:4, ProtoClass:4, 2:8, PtrOpt:8, CalledPartyLen:8, CalledPartyEnc/binary, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100355encode_sccp_msgt(?SCCP_MSGT_CC, Params) ->
356 SrcLocalRef = proplists:get_value(src_local_ref, Params),
357 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welte11565772011-04-15 10:37:57 +0200358 {ProtoClass, PCOpt} = proplists:get_value(protocol_class, Params),
Harald Welte9dda4e12012-01-23 16:15:06 +0100359 OptBin = encode_sccp_opts(Params, [credit, called_party_addr, user_data, importance]),
Harald Welte11565772011-04-15 10:37:57 +0200360 <<?SCCP_MSGT_CC:8, DstLocalRef:24/big, SrcLocalRef:24/big, PCOpt:4, ProtoClass:4, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100361encode_sccp_msgt(?SCCP_MSGT_CREF, Params) ->
362 DstLocalRef = proplists:get_value(dst_local_ref, Params),
363 RefusalCause = proplists:get_value(refusal_cause, Params),
Harald Welte9dda4e12012-01-23 16:15:06 +0100364 OptBin = encode_sccp_opts(Params, [called_party_addr, user_data, importance]),
Harald Welte56ee7a62010-12-20 13:34:32 +0100365 <<?SCCP_MSGT_CREF:8, DstLocalRef:24/big, RefusalCause:8, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100366encode_sccp_msgt(?SCCP_MSGT_RLSD, Params) ->
367 SrcLocalRef = proplists:get_value(src_local_ref, Params),
368 DstLocalRef = proplists:get_value(dst_local_ref, Params),
369 ReleaseCause = proplists:get_value(release_cause, Params),
Harald Welte9dda4e12012-01-23 16:15:06 +0100370 OptBin = encode_sccp_opts(Params, [user_data, importance]),
Harald Welte56ee7a62010-12-20 13:34:32 +0100371 <<?SCCP_MSGT_RLSD:8, DstLocalRef:24/big, SrcLocalRef:24/big, ReleaseCause:8, OptBin/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100372encode_sccp_msgt(?SCCP_MSGT_RLC, Params) ->
373 SrcLocalRef = proplists:get_value(src_local_ref, Params),
374 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100375 <<?SCCP_MSGT_RLC:8, DstLocalRef:24/big, SrcLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100376encode_sccp_msgt(?SCCP_MSGT_DT1, Params) ->
377 DstLocalRef = proplists:get_value(dst_local_ref, Params),
378 SegmReass = proplists:get_value(segm_reass, Params),
Harald Welted9c318f2011-12-10 22:17:11 +0100379 UserData = binarify(proplists:get_value(user_data, Params)),
Harald Weltec0696b02010-12-20 00:09:37 +0100380 UserDataLen = byte_size(UserData),
Harald Welte56ee7a62010-12-20 13:34:32 +0100381 <<?SCCP_MSGT_DT1:8, DstLocalRef:24/big, SegmReass:8, 1:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100382encode_sccp_msgt(?SCCP_MSGT_DT2, Params) ->
383 DstLocalRef = proplists:get_value(dst_local_ref, Params),
384 SeqSegm = proplists:get_value(seq_segm, Params),
Harald Welted9c318f2011-12-10 22:17:11 +0100385 UserData = binarify(proplists:get_value(user_data, Params)),
Harald Weltec0696b02010-12-20 00:09:37 +0100386 UserDataLen = byte_size(UserData),
Harald Welte56ee7a62010-12-20 13:34:32 +0100387 <<?SCCP_MSGT_DT2:8, DstLocalRef:24/big, SeqSegm:16, 1:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100388encode_sccp_msgt(?SCCP_MSGT_AK, Params) ->
389 DstLocalRef = proplists:get_value(dst_local_ref, Params),
390 RxSeqnr = proplists:get_value(rx_seqnr, Params),
391 Credit = proplists:get_value(credit, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100392 <<?SCCP_MSGT_AK:8, DstLocalRef:24/big, RxSeqnr:8, Credit:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100393encode_sccp_msgt(?SCCP_MSGT_UDT, Params) ->
Harald Welte11565772011-04-15 10:37:57 +0200394 {ProtoClass, PCOpt} = proplists:get_value(protocol_class, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100395 CalledParty = proplists:get_value(called_party_addr, Params),
Harald Welte234c9562011-02-03 13:51:12 +0100396 CalledPartyEnc = encode_sccp_addr(CalledParty),
397 CalledPartyLen = byte_size(CalledPartyEnc),
Harald Weltec0696b02010-12-20 00:09:37 +0100398 CallingParty = proplists:get_value(calling_party_addr, Params),
Harald Welte234c9562011-02-03 13:51:12 +0100399 CallingPartyEnc = encode_sccp_addr(CallingParty),
400 CallingPartyLen = byte_size(CallingPartyEnc),
Harald Welted9c318f2011-12-10 22:17:11 +0100401 UserData = binarify(proplists:get_value(user_data, Params)),
Harald Weltec0696b02010-12-20 00:09:37 +0100402 UserDataLen = byte_size(UserData),
403 % variable part
404 CalledPartyPtr = 3,
405 CallingPartyPtr = 2 + (1 + CalledPartyLen),
406 DataPtr = 1 + (1 + CalledPartyLen) + (1 + CallingPartyLen),
Harald Welte234c9562011-02-03 13:51:12 +0100407 Remain = <<CalledPartyLen:8, CalledPartyEnc/binary,
408 CallingPartyLen:8, CallingPartyEnc/binary,
Harald Weltec0696b02010-12-20 00:09:37 +0100409 UserDataLen:8, UserData/binary>>,
Harald Welte11565772011-04-15 10:37:57 +0200410 <<?SCCP_MSGT_UDT:8, PCOpt:4, ProtoClass:4, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary>>;
Harald Welte030f1092011-03-11 19:08:07 +0100411encode_sccp_msgt(?SCCP_MSGT_UDTS, Params) ->
412 ReturnCause = proplists:get_value(return_cause, Params),
413 CalledParty = proplists:get_value(called_party_addr, Params),
414 CalledPartyEnc = encode_sccp_addr(CalledParty),
415 CalledPartyLen = byte_size(CalledPartyEnc),
416 CallingParty = proplists:get_value(calling_party_addr, Params),
417 CallingPartyEnc = encode_sccp_addr(CallingParty),
418 CallingPartyLen = byte_size(CallingPartyEnc),
Harald Welted9c318f2011-12-10 22:17:11 +0100419 UserData = binarify(proplists:get_value(user_data, Params)),
Harald Welte030f1092011-03-11 19:08:07 +0100420 UserDataLen = byte_size(UserData),
421 % variable part
422 CalledPartyPtr = 3,
423 CallingPartyPtr = 2 + (1 + CalledPartyLen),
424 DataPtr = 1 + (1 + CalledPartyLen) + (1 + CallingPartyLen),
425 Remain = <<CalledPartyLen:8, CalledPartyEnc/binary,
426 CallingPartyLen:8, CallingPartyEnc/binary,
427 UserDataLen:8, UserData/binary>>,
428 <<?SCCP_MSGT_UDTS:8, ReturnCause:8, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100429encode_sccp_msgt(?SCCP_MSGT_ED, Params) ->
430 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welted9c318f2011-12-10 22:17:11 +0100431 UserData = binarify(proplists:get_value(user_data, Params)),
Harald Weltec0696b02010-12-20 00:09:37 +0100432 UserDataLen = byte_size(UserData),
433 DataPtr = 1,
Harald Welte56ee7a62010-12-20 13:34:32 +0100434 <<?SCCP_MSGT_ED:8, DstLocalRef:24/big, DataPtr:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100435encode_sccp_msgt(?SCCP_MSGT_EA, Params) ->
436 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100437 <<?SCCP_MSGT_EA:8, DstLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100438encode_sccp_msgt(?SCCP_MSGT_RSR, Params) ->
439 DstLocalRef = proplists:get_value(dst_local_ref, Params),
440 SrcLocalRef = proplists:get_value(src_local_ref, Params),
441 ResetCause = proplists:get_value(reset_cause, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100442 <<?SCCP_MSGT_RSR:8, DstLocalRef:24/big, SrcLocalRef:24/big, ResetCause:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100443encode_sccp_msgt(?SCCP_MSGT_RSC, Params) ->
444 DstLocalRef = proplists:get_value(dst_local_ref, Params),
445 SrcLocalRef = proplists:get_value(src_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100446 <<?SCCP_MSGT_RSC:8, DstLocalRef:24/big, SrcLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100447encode_sccp_msgt(?SCCP_MSGT_ERR, Params) ->
448 DstLocalRef = proplists:get_value(dst_local_ref, Params),
449 ErrCause = proplists:get_value(error_cause, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100450 <<?SCCP_MSGT_ERR:8, DstLocalRef:24/big, ErrCause:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100451encode_sccp_msgt(?SCCP_MSGT_IT, Params) ->
452 DstLocalRef = proplists:get_value(dst_local_ref, Params),
453 SrcLocalRef = proplists:get_value(src_local_ref, Params),
Harald Welte11565772011-04-15 10:37:57 +0200454 {ProtoClass, PCOpt} = proplists:get_value(protocol_class, Params),
Harald Welte09b43992010-12-20 12:21:03 +0100455 SegmSeq = proplists:get_value(seq_segm, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100456 Credit = proplists:get_value(credit, Params),
Harald Welte11565772011-04-15 10:37:57 +0200457 <<?SCCP_MSGT_IT:8, DstLocalRef:24/big, SrcLocalRef:24/big, PCOpt:4, ProtoClass:4, SegmSeq:16, Credit:8>>.
Harald Weltec0696b02010-12-20 00:09:37 +0100458% FIXME: XUDT/XUDTS, LUDT/LUDTS
459
Harald Welte033cef02010-12-19 22:47:14 +0100460
461% encode one sccp message data structure into the on-wire format
462encode_sccp_msg(#sccp_msg{msg_type = MsgType, parameters = Params}) ->
463 encode_sccp_msgt(MsgType, Params).
Harald Welte9abbbad2011-04-21 12:19:41 +0200464
465% is the supplied message type a connectionless message?
466is_connectionless(#sccp_msg{msg_type = MsgType}) ->
467 is_connectionless(MsgType);
468is_connectionless(MsgType) ->
469 case MsgType of
470 ?SCCP_MSGT_UDT -> true;
471 ?SCCP_MSGT_UDTS -> true;
472 ?SCCP_MSGT_XUDT -> true;
473 ?SCCP_MSGT_XUDTS -> true;
474 ?SCCP_MSGT_LUDT -> true;
475 ?SCCP_MSGT_LUDTS -> true;
476 _ -> false
477 end.
Harald Welte9baab6d2011-12-08 00:46:00 +0100478
479
480gen_gt_helper(Number) when is_list(Number) ->
481 #global_title{gti=?SCCP_GTI_NAT_ONLY,
482 nature_of_addr_ind=?SCCP_NAI_INTERNATIONAL,
483 phone_number = Number}.
484
485gen_addr_helper(Gt, Pc, Ssn) when is_record(Gt, global_title) ->
486 #sccp_addr{point_code=Pc, ssn=Ssn, global_title=Gt};
487gen_addr_helper(Number, Pc, Ssn) when is_list(Number) ->
488 Gt = gen_gt_helper(Number),
489 gen_addr_helper(Gt, Pc, Ssn).
490
491
492gen_addr_helper(Gt, Pc) when is_record(Gt, global_title) ->
493 #sccp_addr{point_code=Pc, global_title=Gt};
494gen_addr_helper(Number, Pc) when is_list(Number) ->
495 Gt = gen_gt_helper(Number),
496 gen_addr_helper(Gt, Pc).
Harald Welte9dda4e12012-01-23 16:15:06 +0100497
498opt_to_atom(Num) ->
499 case Num of
500 ?SCCP_PNC_DESTINATION_LOCAL_REFERENCE -> dst_local_ref;
501 ?SCCP_PNC_SOURCE_LOCAL_REFERENCE -> src_local_ref;
502 ?SCCP_PNC_CALLED_PARTY_ADDRESS -> called_party_addr;
503 ?SCCP_PNC_CALLING_PARTY_ADDRESS -> calling_party_addr;
504 ?SCCP_PNC_PROTOCOL_CLASS -> protocol_class;
505 ?SCCP_PNC_SEGMENTING -> segmenting;
506 ?SCCP_PNC_RECEIVE_SEQ_NUMBER -> rx_seq_number;
507 ?SCCP_PNC_SEQUENCING -> seq_segm;
508 ?SCCP_PNC_CREDIT -> credit;
509 ?SCCP_PNC_RELEASE_CAUSE -> release_cause;
510 ?SCCP_PNC_RETURN_CAUSE -> return_cause;
511 ?SCCP_PNC_RESET_CAUSE -> reset_cause;
512 ?SCCP_PNC_ERROR_CAUSE -> error_cause;
513 ?SCCP_PNC_REFUSAL_CAUSE -> refusal_cause;
514 ?SCCP_PNC_DATA -> user_data;
515 ?SCCP_PNC_SEGMENTATION -> segmentation;
516 ?SCCP_PNC_HOP_COUNTER -> hop_counter;
517 ?SCCP_PNC_IMPORTANCE -> importance;
518 ?SCCP_PNC_LONG_DATA -> long_data;
519 Foo -> Foo
520 end.
521
522atom_to_opt(Atom) ->
523 case Atom of
524 dst_local_ref -> ?SCCP_PNC_DESTINATION_LOCAL_REFERENCE;
525 src_local_ref -> ?SCCP_PNC_SOURCE_LOCAL_REFERENCE;
526 called_party_addr -> ?SCCP_PNC_CALLED_PARTY_ADDRESS;
527 calling_party_addr -> ?SCCP_PNC_CALLING_PARTY_ADDRESS;
528 protocol_class -> ?SCCP_PNC_PROTOCOL_CLASS;
529 segmenting -> ?SCCP_PNC_SEGMENTING;
530 rx_seq_number -> ?SCCP_PNC_RECEIVE_SEQ_NUMBER;
531 seq_segm -> ?SCCP_PNC_SEQUENCING;
532 credit -> ?SCCP_PNC_CREDIT;
533 release_cause -> ?SCCP_PNC_RELEASE_CAUSE;
534 return_cause -> ?SCCP_PNC_RETURN_CAUSE;
535 reset_cause -> ?SCCP_PNC_RESET_CAUSE;
536 error_cause -> ?SCCP_PNC_ERROR_CAUSE;
537 refusal_cause -> ?SCCP_PNC_REFUSAL_CAUSE;
538 user_data -> ?SCCP_PNC_DATA;
539 segmentation -> ?SCCP_PNC_SEGMENTATION;
540 hop_counter -> ?SCCP_PNC_HOP_COUNTER;
541 importance -> ?SCCP_PNC_IMPORTANCE;
542 long_data -> ?SCCP_PNC_LONG_DATA;
543 Foo -> Foo
544 end.