blob: 62e5bdc3d2f31d51fa502054863279248526776b [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
24-export([parse_sccp_msg/1, encode_sccp_msg/1, encode_sccp_msgt/2]).
25
26-compile(export_all).
27
Harald Welteba6fdbb2011-01-23 22:04:39 +010028parse_point_code(BinPC, PCind, OptListIn) when is_binary(BinPC),
29 is_list(OptListIn) ->
30 case PCind of
31 1 ->
32 <<PointCode:16/big, Remain/binary>> = BinPC,
33 OptListOut = OptListIn ++ [{point_code, PointCode}];
34 _ ->
35 Remain = BinPC,
36 OptListOut = OptListIn
37 end,
38 {Remain, OptListOut}.
39
40parse_ssn(BinSSN, SSNind, OptListIn) ->
41 case SSNind of
42 1 ->
43 <<SSN:8, Remain/binary>> = BinSSN,
44 OptListOut = OptListIn ++ [{ssn, SSN}];
45 _ ->
46 Remain = BinSSN,
47 OptListOut = OptListIn
48 end,
49 {Remain, OptListOut}.
50
51enc_is_odd(Enc) ->
52 case Enc of
53 1 -> 1;
54 _ -> 0
55 end.
56
57parse_gt(BinGT, GTind, OptListIn) ->
58 case GTind of
59 ?SCCP_GTI_NO_GT ->
60 NewOpts = [];
61 ?SCCP_GTI_NAT_ONLY ->
62 % Figure 7/Q.713
63 <<Odd:1, Nature:7, Digits/binary>> = BinGT,
64 PhoneNum = isup_codec:parse_isup_party(Digits, Odd),
65 NewOpts = [{nature_of_addr_ind, Nature},
66 {phone_number, PhoneNum}];
67 ?SCCP_GTI_TT_ONLY ->
68 % Figure 9/Q.913
69 <<TransType:8, Digits/binary>> = BinGT,
70 % Used in national interfaces only, we cannot parse Digits
71 NewOpts = [{trans_type, TransType}, {address, Digits}];
72 ?SCCP_GTI_TT_NP_ENC ->
73 % Figure 10/Q.713
74 <<TransType:8, NumPlan:4, Enc:4, Digits/binary>> = BinGT,
75 PhoneNum = isup_codec:parse_isup_party(Digits, enc_is_odd(Enc)),
76 NewOpts = [{trans_type, TransType}, {encoding, Enc},
77 {numbering_plan, NumPlan},
78 {phone_number, PhoneNum}];
79 ?SCCP_GTI_TT_NP_ENC_NAT ->
80 % Figure 11/Q.713
81 <<TransType:8, NumPlan:4, Enc:4, 0:1, Nature:7, Digits/binary>> = BinGT,
82 PhoneNum = isup_codec:parse_isup_party(Digits, enc_is_odd(Enc)),
83 NewOpts = [{trans_type, TransType}, {encoding, Enc},
84 {numbering_plan, NumPlan},
85 {nature_of_addr_ind, Nature},
86 {phone_number, PhoneNum}];
87 _ ->
88 NewOpts = [{unknown, BinGT}]
89 end,
90 OptListIn ++ [{global_title, GTind, NewOpts}].
91
92% parse SCCP Address
93parse_sccp_addr(BinAddr) when is_binary(BinAddr) ->
94 <<ResNatUse:1, RoutInd:1, GTind:4, SSNind:1, PCind:1, Remain/binary>> = BinAddr,
95 OptList = [{reserved_national_use, ResNatUse}, {route_on_ssn, RoutInd}],
96 {RemainPC, OptPC} = parse_point_code(Remain, PCind, OptList),
97 {RemainSSN, OptSSN} = parse_ssn(RemainPC, SSNind, OptPC),
98 OptGT = parse_gt(RemainSSN, GTind, OptSSN),
99 OptGT.
Harald Welte033cef02010-12-19 22:47:14 +0100100
101% parse SCCP Optional Part
102parse_sccp_opt(OptType, OptLen, Content) ->
103 {OptType, {OptLen, Content}}.
104
105parse_sccp_opts(<<>>, OptList) ->
106 % empty list
107 OptList;
108parse_sccp_opts(<<0>>, OptList) ->
109 % end of options
110 OptList;
111parse_sccp_opts(OptBin, OptList) ->
112 <<OptType, OptLen, Content:OptLen/binary, Remain/binary>> = OptBin,
113 NewOpt = parse_sccp_opt(OptType, OptLen, Content),
114 parse_sccp_opts(Remain, [NewOpt|OptList]).
115
116% Parse incoming SCCP message, one function for every message type
117parse_sccp_msgt(?SCCP_MSGT_CR, DataBin) ->
118 % first get the fixed part
Harald Welte56ee7a62010-12-20 13:34:32 +0100119 <<_:8, SrcLocalRef:24/big, ProtoClass:8, RemainVar/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100120 % variable length fixed part
121 <<PtrVar:8, PtrOpt:8, _/binary>> = RemainVar,
122 CalledPartyLen = binary:at(RemainVar, PtrVar),
123 CalledParty = binary:part(RemainVar, PtrVar+1, CalledPartyLen),
124 % optional part
125 OptBin = binary:part(RemainVar, 1 + PtrOpt, byte_size(RemainVar)-(1+PtrOpt)),
126 OptList = parse_sccp_opts(OptBin, []),
127 %OptList = [],
128 % build parsed list of message
129 [{src_local_ref, SrcLocalRef},{protocol_class, ProtoClass},{called_party_addr, CalledParty}|OptList];
130parse_sccp_msgt(?SCCP_MSGT_CC, DataBin) ->
131 % first get the fixed part
Harald Welte56ee7a62010-12-20 13:34:32 +0100132 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100133 % optional part
134 OptList = parse_sccp_opts(Remain, []),
135 % build parsed list of message
136 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{protocol_class, ProtoClass}|OptList];
137parse_sccp_msgt(?SCCP_MSGT_CREF, DataBin) ->
138 % first get the fixed part
Harald Welte56ee7a62010-12-20 13:34:32 +0100139 <<_:8, DstLocalRef:24/big, RefusalCause:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100140 % optional part
141 OptList = parse_sccp_opts(Remain, []),
142 % build parsed list of message
143 [{dst_local_ref, DstLocalRef},{refusal_cause, RefusalCause}|OptList];
144parse_sccp_msgt(?SCCP_MSGT_RLSD, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100145 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ReleaseCause:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100146 % optional part
147 OptList = parse_sccp_opts(Remain, []),
148 % build parsed list of message
149 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{release_cause, ReleaseCause}|OptList];
150parse_sccp_msgt(?SCCP_MSGT_RLC, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100151 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100152 % build parsed list of message
153 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef}];
154parse_sccp_msgt(?SCCP_MSGT_DT1, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100155 <<_:8, DstLocalRef:24/big, SegmReass:8, DataPtr:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100156 DataLen = binary:at(Remain, DataPtr-1),
157 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
158 % build parsed list of message
159 [{dst_local_ref, DstLocalRef},{segm_reass, SegmReass},{user_data, UserData}];
160parse_sccp_msgt(?SCCP_MSGT_DT2, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100161 <<_:8, DstLocalRef:24/big, SeqSegm:16, DataPtr:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100162 DataLen = binary:at(Remain, DataPtr-1),
163 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
164 % build parsed list of message
165 [{dst_local_ref, DstLocalRef},{seq_segm, SeqSegm},{user_data, UserData}];
166parse_sccp_msgt(?SCCP_MSGT_AK, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100167 <<_:8, DstLocalRef:24/big, RxSeqnr:8, Credit:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100168 [{dst_local_ref, DstLocalRef},{rx_seq_nr, RxSeqnr},{credit, Credit}];
169parse_sccp_msgt(?SCCP_MSGT_UDT, DataBin) ->
170 <<_:8, ProtoClass:8, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary >> = DataBin,
171 % variable part
172 CalledPartyLen = binary:at(Remain, CalledPartyPtr-3),
173 CalledParty = binary:part(Remain, CalledPartyPtr-3+1, CalledPartyLen),
174 CallingPartyLen = binary:at(Remain, CallingPartyPtr-2),
175 CallingParty = binary:part(Remain, CallingPartyPtr-2+1, CallingPartyLen),
176 DataLen = binary:at(Remain, DataPtr-1),
177 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
178 [{protocol_class, ProtoClass},{called_party_addr, CalledParty},
179 {calling_party_addr, CallingParty},{user_data, UserData}];
180parse_sccp_msgt(?SCCP_MSGT_UDTS, DataBin) ->
181 parse_sccp_msgt(?SCCP_MSGT_UDT, DataBin);
182parse_sccp_msgt(?SCCP_MSGT_ED, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100183 <<_:8, DstLocalRef:24/big, DataPtr:8, Remain/binary>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100184 DataLen = binary:at(Remain, DataPtr-1),
185 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
186 [{dst_local_ref, DstLocalRef}, {user_data, UserData}];
187parse_sccp_msgt(?SCCP_MSGT_EA, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100188 <<_:8, DstLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100189 [{dst_local_ref, DstLocalRef}];
190parse_sccp_msgt(?SCCP_MSGT_RSR, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100191 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ResetCause:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100192 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{reset_cause, ResetCause}];
193parse_sccp_msgt(?SCCP_MSGT_RSC, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100194 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100195 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef}];
196parse_sccp_msgt(?SCCP_MSGT_ERR, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100197 <<_:8, DstLocalRef:24/big, ErrCause:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100198 [{dst_local_ref, DstLocalRef},{error_cause, ErrCause}];
199parse_sccp_msgt(?SCCP_MSGT_IT, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100200 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, SegmSeq:16, Credit:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100201 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},
202 {protocol_class, ProtoClass},{seq_segm, SegmSeq},{credit, Credit}].
203% FIXME: XUDT/XUDTS, LUDT/LUDTS
204
205% process one incoming SCCP message
206parse_sccp_msg(DataBin) ->
207 MsgType = binary:first(DataBin),
208 Parsed = parse_sccp_msgt(MsgType, DataBin),
209 {ok, #sccp_msg{msg_type = MsgType, parameters = Parsed}}.
210
211% Encoding Part
212
213encode_sccp_opt({OptNum, {DataBinLen, DataBin}}) when is_integer(OptNum) ->
214 DataBinLen8 = DataBinLen*8,
215 <<OptNum:8, DataBinLen:8, DataBin:DataBinLen8>>;
216encode_sccp_opt({OptAtom,_}) when is_atom(OptAtom) ->
217 <<>>.
218
219encode_sccp_opts([], OptEnc) ->
220 % end of options + convert to binary
221 list_to_binary([OptEnc, ?SCCP_PNC_END_OF_OPTIONAL]);
222encode_sccp_opts([CurOpt|OptPropList], OptEnc) ->
223 CurOptEnc = encode_sccp_opt(CurOpt),
224 encode_sccp_opts(OptPropList, list_to_binary([OptEnc,CurOptEnc])).
225
226
227
228encode_sccp_msgt(?SCCP_MSGT_CR, Params) ->
229 SrcLocalRef = proplists:get_value(src_local_ref, Params),
230 ProtoClass = proplists:get_value(protocol_class, Params),
231 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100232 <<?SCCP_MSGT_CR:8, SrcLocalRef:24/big, ProtoClass:8, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100233encode_sccp_msgt(?SCCP_MSGT_CC, Params) ->
234 SrcLocalRef = proplists:get_value(src_local_ref, Params),
235 DstLocalRef = proplists:get_value(dst_local_ref, Params),
236 ProtoClass = proplists:get_value(protocol_class, Params),
237 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100238 <<?SCCP_MSGT_CC:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100239encode_sccp_msgt(?SCCP_MSGT_CREF, Params) ->
240 DstLocalRef = proplists:get_value(dst_local_ref, Params),
241 RefusalCause = proplists:get_value(refusal_cause, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100242 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100243 <<?SCCP_MSGT_CREF:8, DstLocalRef:24/big, RefusalCause:8, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100244encode_sccp_msgt(?SCCP_MSGT_RLSD, Params) ->
245 SrcLocalRef = proplists:get_value(src_local_ref, Params),
246 DstLocalRef = proplists:get_value(dst_local_ref, Params),
247 ReleaseCause = proplists:get_value(release_cause, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100248 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100249 <<?SCCP_MSGT_RLSD:8, DstLocalRef:24/big, SrcLocalRef:24/big, ReleaseCause:8, OptBin/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100250encode_sccp_msgt(?SCCP_MSGT_RLC, Params) ->
251 SrcLocalRef = proplists:get_value(src_local_ref, Params),
252 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100253 <<?SCCP_MSGT_RLC:8, DstLocalRef:24/big, SrcLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100254encode_sccp_msgt(?SCCP_MSGT_DT1, Params) ->
255 DstLocalRef = proplists:get_value(dst_local_ref, Params),
256 SegmReass = proplists:get_value(segm_reass, Params),
257 UserData = proplists:get_value(user_data, Params),
258 UserDataLen = byte_size(UserData),
Harald Welte56ee7a62010-12-20 13:34:32 +0100259 <<?SCCP_MSGT_DT1:8, DstLocalRef:24/big, SegmReass:8, 1:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100260encode_sccp_msgt(?SCCP_MSGT_DT2, Params) ->
261 DstLocalRef = proplists:get_value(dst_local_ref, Params),
262 SeqSegm = proplists:get_value(seq_segm, Params),
263 UserData = proplists:get_value(user_data, Params),
264 UserDataLen = byte_size(UserData),
Harald Welte56ee7a62010-12-20 13:34:32 +0100265 <<?SCCP_MSGT_DT2:8, DstLocalRef:24/big, SeqSegm:16, 1:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100266encode_sccp_msgt(?SCCP_MSGT_AK, Params) ->
267 DstLocalRef = proplists:get_value(dst_local_ref, Params),
268 RxSeqnr = proplists:get_value(rx_seqnr, Params),
269 Credit = proplists:get_value(credit, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100270 <<?SCCP_MSGT_AK:8, DstLocalRef:24/big, RxSeqnr:8, Credit:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100271encode_sccp_msgt(?SCCP_MSGT_UDT, Params) ->
272 ProtoClass = proplists:get_value(protocol_class, Params),
273 CalledParty = proplists:get_value(called_party_addr, Params),
274 CalledPartyLen = byte_size(CalledParty),
275 CallingParty = proplists:get_value(calling_party_addr, Params),
276 CallingPartyLen = byte_size(CallingParty),
277 UserData = proplists:get_value(user_data, Params),
278 UserDataLen = byte_size(UserData),
279 % variable part
280 CalledPartyPtr = 3,
281 CallingPartyPtr = 2 + (1 + CalledPartyLen),
282 DataPtr = 1 + (1 + CalledPartyLen) + (1 + CallingPartyLen),
283 Remain = <<CalledPartyLen:8, CalledParty/binary,
284 CallingPartyLen:8, CallingParty/binary,
285 UserDataLen:8, UserData/binary>>,
286 <<?SCCP_MSGT_UDT:8, ProtoClass:8, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary>>;
287%encode_sccp_msgt(?SCCP_MSGT_UDTS, Params) ->
288 % FIXME !!!
289encode_sccp_msgt(?SCCP_MSGT_ED, Params) ->
290 DstLocalRef = proplists:get_value(dst_local_ref, Params),
291 UserData = proplists:get_value(user_data, Params),
292 UserDataLen = byte_size(UserData),
293 DataPtr = 1,
Harald Welte56ee7a62010-12-20 13:34:32 +0100294 <<?SCCP_MSGT_ED:8, DstLocalRef:24/big, DataPtr:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100295encode_sccp_msgt(?SCCP_MSGT_EA, Params) ->
296 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100297 <<?SCCP_MSGT_EA:8, DstLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100298encode_sccp_msgt(?SCCP_MSGT_RSR, Params) ->
299 DstLocalRef = proplists:get_value(dst_local_ref, Params),
300 SrcLocalRef = proplists:get_value(src_local_ref, Params),
301 ResetCause = proplists:get_value(reset_cause, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100302 <<?SCCP_MSGT_RSR:8, DstLocalRef:24/big, SrcLocalRef:24/big, ResetCause:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100303encode_sccp_msgt(?SCCP_MSGT_RSC, Params) ->
304 DstLocalRef = proplists:get_value(dst_local_ref, Params),
305 SrcLocalRef = proplists:get_value(src_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100306 <<?SCCP_MSGT_RSC:8, DstLocalRef:24/big, SrcLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100307encode_sccp_msgt(?SCCP_MSGT_ERR, Params) ->
308 DstLocalRef = proplists:get_value(dst_local_ref, Params),
309 ErrCause = proplists:get_value(error_cause, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100310 <<?SCCP_MSGT_ERR:8, DstLocalRef:24/big, ErrCause:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100311encode_sccp_msgt(?SCCP_MSGT_IT, Params) ->
312 DstLocalRef = proplists:get_value(dst_local_ref, Params),
313 SrcLocalRef = proplists:get_value(src_local_ref, Params),
314 ProtoClass = proplists:get_value(protocol_class, Params),
Harald Welte09b43992010-12-20 12:21:03 +0100315 SegmSeq = proplists:get_value(seq_segm, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100316 Credit = proplists:get_value(credit, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100317 <<?SCCP_MSGT_IT:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, SegmSeq:16, Credit:8>>.
Harald Weltec0696b02010-12-20 00:09:37 +0100318% FIXME: XUDT/XUDTS, LUDT/LUDTS
319
Harald Welte033cef02010-12-19 22:47:14 +0100320
321% encode one sccp message data structure into the on-wire format
322encode_sccp_msg(#sccp_msg{msg_type = MsgType, parameters = Params}) ->
323 encode_sccp_msgt(MsgType, Params).