blob: f5854e17d39f53468505f5ca83d567c9030759b2 [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
28
29% parse SCCP Optional Part
30parse_sccp_opt(OptType, OptLen, Content) ->
31 {OptType, {OptLen, Content}}.
32
33parse_sccp_opts(<<>>, OptList) ->
34 % empty list
35 OptList;
36parse_sccp_opts(<<0>>, OptList) ->
37 % end of options
38 OptList;
39parse_sccp_opts(OptBin, OptList) ->
40 <<OptType, OptLen, Content:OptLen/binary, Remain/binary>> = OptBin,
41 NewOpt = parse_sccp_opt(OptType, OptLen, Content),
42 parse_sccp_opts(Remain, [NewOpt|OptList]).
43
44% Parse incoming SCCP message, one function for every message type
45parse_sccp_msgt(?SCCP_MSGT_CR, DataBin) ->
46 % first get the fixed part
Harald Welte56ee7a62010-12-20 13:34:32 +010047 <<_:8, SrcLocalRef:24/big, ProtoClass:8, RemainVar/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010048 % variable length fixed part
49 <<PtrVar:8, PtrOpt:8, _/binary>> = RemainVar,
50 CalledPartyLen = binary:at(RemainVar, PtrVar),
51 CalledParty = binary:part(RemainVar, PtrVar+1, CalledPartyLen),
52 % optional part
53 OptBin = binary:part(RemainVar, 1 + PtrOpt, byte_size(RemainVar)-(1+PtrOpt)),
54 OptList = parse_sccp_opts(OptBin, []),
55 %OptList = [],
56 % build parsed list of message
57 [{src_local_ref, SrcLocalRef},{protocol_class, ProtoClass},{called_party_addr, CalledParty}|OptList];
58parse_sccp_msgt(?SCCP_MSGT_CC, DataBin) ->
59 % first get the fixed part
Harald Welte56ee7a62010-12-20 13:34:32 +010060 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010061 % optional part
62 OptList = parse_sccp_opts(Remain, []),
63 % build parsed list of message
64 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{protocol_class, ProtoClass}|OptList];
65parse_sccp_msgt(?SCCP_MSGT_CREF, DataBin) ->
66 % first get the fixed part
Harald Welte56ee7a62010-12-20 13:34:32 +010067 <<_:8, DstLocalRef:24/big, RefusalCause:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010068 % optional part
69 OptList = parse_sccp_opts(Remain, []),
70 % build parsed list of message
71 [{dst_local_ref, DstLocalRef},{refusal_cause, RefusalCause}|OptList];
72parse_sccp_msgt(?SCCP_MSGT_RLSD, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +010073 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ReleaseCause:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010074 % optional part
75 OptList = parse_sccp_opts(Remain, []),
76 % build parsed list of message
77 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{release_cause, ReleaseCause}|OptList];
78parse_sccp_msgt(?SCCP_MSGT_RLC, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +010079 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010080 % build parsed list of message
81 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef}];
82parse_sccp_msgt(?SCCP_MSGT_DT1, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +010083 <<_:8, DstLocalRef:24/big, SegmReass:8, DataPtr:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010084 DataLen = binary:at(Remain, DataPtr-1),
85 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
86 % build parsed list of message
87 [{dst_local_ref, DstLocalRef},{segm_reass, SegmReass},{user_data, UserData}];
88parse_sccp_msgt(?SCCP_MSGT_DT2, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +010089 <<_:8, DstLocalRef:24/big, SeqSegm:16, DataPtr:8, Remain/binary >> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010090 DataLen = binary:at(Remain, DataPtr-1),
91 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
92 % build parsed list of message
93 [{dst_local_ref, DstLocalRef},{seq_segm, SeqSegm},{user_data, UserData}];
94parse_sccp_msgt(?SCCP_MSGT_AK, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +010095 <<_:8, DstLocalRef:24/big, RxSeqnr:8, Credit:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +010096 [{dst_local_ref, DstLocalRef},{rx_seq_nr, RxSeqnr},{credit, Credit}];
97parse_sccp_msgt(?SCCP_MSGT_UDT, DataBin) ->
98 <<_:8, ProtoClass:8, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary >> = DataBin,
99 % variable part
100 CalledPartyLen = binary:at(Remain, CalledPartyPtr-3),
101 CalledParty = binary:part(Remain, CalledPartyPtr-3+1, CalledPartyLen),
102 CallingPartyLen = binary:at(Remain, CallingPartyPtr-2),
103 CallingParty = binary:part(Remain, CallingPartyPtr-2+1, CallingPartyLen),
104 DataLen = binary:at(Remain, DataPtr-1),
105 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
106 [{protocol_class, ProtoClass},{called_party_addr, CalledParty},
107 {calling_party_addr, CallingParty},{user_data, UserData}];
108parse_sccp_msgt(?SCCP_MSGT_UDTS, DataBin) ->
109 parse_sccp_msgt(?SCCP_MSGT_UDT, DataBin);
110parse_sccp_msgt(?SCCP_MSGT_ED, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100111 <<_:8, DstLocalRef:24/big, DataPtr:8, Remain/binary>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100112 DataLen = binary:at(Remain, DataPtr-1),
113 UserData = binary:part(Remain, DataPtr-1+1, DataLen),
114 [{dst_local_ref, DstLocalRef}, {user_data, UserData}];
115parse_sccp_msgt(?SCCP_MSGT_EA, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100116 <<_:8, DstLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100117 [{dst_local_ref, DstLocalRef}];
118parse_sccp_msgt(?SCCP_MSGT_RSR, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100119 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ResetCause:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100120 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},{reset_cause, ResetCause}];
121parse_sccp_msgt(?SCCP_MSGT_RSC, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100122 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100123 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef}];
124parse_sccp_msgt(?SCCP_MSGT_ERR, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100125 <<_:8, DstLocalRef:24/big, ErrCause:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100126 [{dst_local_ref, DstLocalRef},{error_cause, ErrCause}];
127parse_sccp_msgt(?SCCP_MSGT_IT, DataBin) ->
Harald Welte56ee7a62010-12-20 13:34:32 +0100128 <<_:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, SegmSeq:16, Credit:8>> = DataBin,
Harald Welte033cef02010-12-19 22:47:14 +0100129 [{dst_local_ref, DstLocalRef},{src_local_ref, SrcLocalRef},
130 {protocol_class, ProtoClass},{seq_segm, SegmSeq},{credit, Credit}].
131% FIXME: XUDT/XUDTS, LUDT/LUDTS
132
133% process one incoming SCCP message
134parse_sccp_msg(DataBin) ->
135 MsgType = binary:first(DataBin),
136 Parsed = parse_sccp_msgt(MsgType, DataBin),
137 {ok, #sccp_msg{msg_type = MsgType, parameters = Parsed}}.
138
139% Encoding Part
140
141encode_sccp_opt({OptNum, {DataBinLen, DataBin}}) when is_integer(OptNum) ->
142 DataBinLen8 = DataBinLen*8,
143 <<OptNum:8, DataBinLen:8, DataBin:DataBinLen8>>;
144encode_sccp_opt({OptAtom,_}) when is_atom(OptAtom) ->
145 <<>>.
146
147encode_sccp_opts([], OptEnc) ->
148 % end of options + convert to binary
149 list_to_binary([OptEnc, ?SCCP_PNC_END_OF_OPTIONAL]);
150encode_sccp_opts([CurOpt|OptPropList], OptEnc) ->
151 CurOptEnc = encode_sccp_opt(CurOpt),
152 encode_sccp_opts(OptPropList, list_to_binary([OptEnc,CurOptEnc])).
153
154
155
156encode_sccp_msgt(?SCCP_MSGT_CR, Params) ->
157 SrcLocalRef = proplists:get_value(src_local_ref, Params),
158 ProtoClass = proplists:get_value(protocol_class, Params),
159 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100160 <<?SCCP_MSGT_CR:8, SrcLocalRef:24/big, ProtoClass:8, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100161encode_sccp_msgt(?SCCP_MSGT_CC, Params) ->
162 SrcLocalRef = proplists:get_value(src_local_ref, Params),
163 DstLocalRef = proplists:get_value(dst_local_ref, Params),
164 ProtoClass = proplists:get_value(protocol_class, Params),
165 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100166 <<?SCCP_MSGT_CC:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100167encode_sccp_msgt(?SCCP_MSGT_CREF, Params) ->
168 DstLocalRef = proplists:get_value(dst_local_ref, Params),
169 RefusalCause = proplists:get_value(refusal_cause, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100170 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100171 <<?SCCP_MSGT_CREF:8, DstLocalRef:24/big, RefusalCause:8, OptBin/binary>>;
Harald Welte033cef02010-12-19 22:47:14 +0100172encode_sccp_msgt(?SCCP_MSGT_RLSD, Params) ->
173 SrcLocalRef = proplists:get_value(src_local_ref, Params),
174 DstLocalRef = proplists:get_value(dst_local_ref, Params),
175 ReleaseCause = proplists:get_value(release_cause, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100176 OptBin = encode_sccp_opts(Params, []),
Harald Welte56ee7a62010-12-20 13:34:32 +0100177 <<?SCCP_MSGT_RLSD:8, DstLocalRef:24/big, SrcLocalRef:24/big, ReleaseCause:8, OptBin/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100178encode_sccp_msgt(?SCCP_MSGT_RLC, Params) ->
179 SrcLocalRef = proplists:get_value(src_local_ref, Params),
180 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100181 <<?SCCP_MSGT_RLC:8, DstLocalRef:24/big, SrcLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100182encode_sccp_msgt(?SCCP_MSGT_DT1, Params) ->
183 DstLocalRef = proplists:get_value(dst_local_ref, Params),
184 SegmReass = proplists:get_value(segm_reass, Params),
185 UserData = proplists:get_value(user_data, Params),
186 UserDataLen = byte_size(UserData),
Harald Welte56ee7a62010-12-20 13:34:32 +0100187 <<?SCCP_MSGT_DT1:8, DstLocalRef:24/big, SegmReass:8, 1:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100188encode_sccp_msgt(?SCCP_MSGT_DT2, Params) ->
189 DstLocalRef = proplists:get_value(dst_local_ref, Params),
190 SeqSegm = proplists:get_value(seq_segm, Params),
191 UserData = proplists:get_value(user_data, Params),
192 UserDataLen = byte_size(UserData),
Harald Welte56ee7a62010-12-20 13:34:32 +0100193 <<?SCCP_MSGT_DT2:8, DstLocalRef:24/big, SeqSegm:16, 1:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100194encode_sccp_msgt(?SCCP_MSGT_AK, Params) ->
195 DstLocalRef = proplists:get_value(dst_local_ref, Params),
196 RxSeqnr = proplists:get_value(rx_seqnr, Params),
197 Credit = proplists:get_value(credit, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100198 <<?SCCP_MSGT_AK:8, DstLocalRef:24/big, RxSeqnr:8, Credit:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100199encode_sccp_msgt(?SCCP_MSGT_UDT, Params) ->
200 ProtoClass = proplists:get_value(protocol_class, Params),
201 CalledParty = proplists:get_value(called_party_addr, Params),
202 CalledPartyLen = byte_size(CalledParty),
203 CallingParty = proplists:get_value(calling_party_addr, Params),
204 CallingPartyLen = byte_size(CallingParty),
205 UserData = proplists:get_value(user_data, Params),
206 UserDataLen = byte_size(UserData),
207 % variable part
208 CalledPartyPtr = 3,
209 CallingPartyPtr = 2 + (1 + CalledPartyLen),
210 DataPtr = 1 + (1 + CalledPartyLen) + (1 + CallingPartyLen),
211 Remain = <<CalledPartyLen:8, CalledParty/binary,
212 CallingPartyLen:8, CallingParty/binary,
213 UserDataLen:8, UserData/binary>>,
214 <<?SCCP_MSGT_UDT:8, ProtoClass:8, CalledPartyPtr:8, CallingPartyPtr:8, DataPtr:8, Remain/binary>>;
215%encode_sccp_msgt(?SCCP_MSGT_UDTS, Params) ->
216 % FIXME !!!
217encode_sccp_msgt(?SCCP_MSGT_ED, Params) ->
218 DstLocalRef = proplists:get_value(dst_local_ref, Params),
219 UserData = proplists:get_value(user_data, Params),
220 UserDataLen = byte_size(UserData),
221 DataPtr = 1,
Harald Welte56ee7a62010-12-20 13:34:32 +0100222 <<?SCCP_MSGT_ED:8, DstLocalRef:24/big, DataPtr:8, UserDataLen:8, UserData/binary>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100223encode_sccp_msgt(?SCCP_MSGT_EA, Params) ->
224 DstLocalRef = proplists:get_value(dst_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100225 <<?SCCP_MSGT_EA:8, DstLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100226encode_sccp_msgt(?SCCP_MSGT_RSR, Params) ->
227 DstLocalRef = proplists:get_value(dst_local_ref, Params),
228 SrcLocalRef = proplists:get_value(src_local_ref, Params),
229 ResetCause = proplists:get_value(reset_cause, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100230 <<?SCCP_MSGT_RSR:8, DstLocalRef:24/big, SrcLocalRef:24/big, ResetCause:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100231encode_sccp_msgt(?SCCP_MSGT_RSC, Params) ->
232 DstLocalRef = proplists:get_value(dst_local_ref, Params),
233 SrcLocalRef = proplists:get_value(src_local_ref, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100234 <<?SCCP_MSGT_RSC:8, DstLocalRef:24/big, SrcLocalRef:24/big>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100235encode_sccp_msgt(?SCCP_MSGT_ERR, Params) ->
236 DstLocalRef = proplists:get_value(dst_local_ref, Params),
237 ErrCause = proplists:get_value(error_cause, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100238 <<?SCCP_MSGT_ERR:8, DstLocalRef:24/big, ErrCause:8>>;
Harald Weltec0696b02010-12-20 00:09:37 +0100239encode_sccp_msgt(?SCCP_MSGT_IT, Params) ->
240 DstLocalRef = proplists:get_value(dst_local_ref, Params),
241 SrcLocalRef = proplists:get_value(src_local_ref, Params),
242 ProtoClass = proplists:get_value(protocol_class, Params),
Harald Welte09b43992010-12-20 12:21:03 +0100243 SegmSeq = proplists:get_value(seq_segm, Params),
Harald Weltec0696b02010-12-20 00:09:37 +0100244 Credit = proplists:get_value(credit, Params),
Harald Welte56ee7a62010-12-20 13:34:32 +0100245 <<?SCCP_MSGT_IT:8, DstLocalRef:24/big, SrcLocalRef:24/big, ProtoClass:8, SegmSeq:16, Credit:8>>.
Harald Weltec0696b02010-12-20 00:09:37 +0100246% FIXME: XUDT/XUDTS, LUDT/LUDTS
247
Harald Welte033cef02010-12-19 22:47:14 +0100248
249% encode one sccp message data structure into the on-wire format
250encode_sccp_msg(#sccp_msg{msg_type = MsgType, parameters = Params}) ->
251 encode_sccp_msgt(MsgType, Params).