Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 1 | % Conversion between SUA messages and #sccp_msg{} |
| 2 | |
| 3 | % (C) 2011 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/>. |
Harald Welte | f8bf032 | 2012-04-16 13:10:47 +0200 | [diff] [blame] | 19 | % |
| 20 | % Additional Permission under GNU AGPL version 3 section 7: |
| 21 | % |
| 22 | % If you modify this Program, or any covered work, by linking or |
| 23 | % combining it with runtime libraries of Erlang/OTP as released by |
| 24 | % Ericsson on http://www.erlang.org (or a modified version of these |
| 25 | % libraries), containing parts covered by the terms of the Erlang Public |
| 26 | % License (http://www.erlang.org/EPLICENSE), the licensors of this |
| 27 | % Program grant you additional permission to convey the resulting work |
| 28 | % without the need to license the runtime libraries of Erlang/OTP under |
| 29 | % the GNU Affero General Public License. Corresponding Source for a |
| 30 | % non-source form of such a combination shall include the source code |
| 31 | % for the parts of the runtime libraries of Erlang/OTP used as well as |
| 32 | % that of the covered work. |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 33 | |
| 34 | % FIXME: this currently only supports connection-less SCCP |
| 35 | |
| 36 | -module(sua_sccp_conv). |
| 37 | -author('Harald Welte <laforge@gnumonks.org>'). |
| 38 | |
| 39 | -include("sua.hrl"). |
Harald Welte | 92e783d | 2012-04-01 19:52:01 +0200 | [diff] [blame] | 40 | -include("xua.hrl"). |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 41 | -include("sccp.hrl"). |
| 42 | |
| 43 | -export([sua_to_sccp/1, sccp_to_sua/1]). |
| 44 | |
Harald Welte | 92e783d | 2012-04-01 19:52:01 +0200 | [diff] [blame] | 45 | sua_to_sccp(M=#xua_msg{msg_class = Class, msg_type = Type}) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 46 | sua_to_sccp(Class, Type, M). |
| 47 | sua_to_sccp(?SUA_MSGC_CL, ?SUA_CL_CLDT, Sua) -> |
| 48 | Params = sua_to_sccp_params(Sua), |
| 49 | #sccp_msg{msg_type = ?SCCP_MSGT_UDT, |
| 50 | parameters = Params}; |
| 51 | sua_to_sccp(?SUA_MSGC_CL, ?SUA_CL_CLDR, Sua) -> |
| 52 | Params = sua_to_sccp_params(Sua), |
| 53 | #sccp_msg{msg_type = ?SCCP_MSGT_UDTS, |
| 54 | parameters = Params}. |
| 55 | |
Harald Welte | 99efaf9 | 2012-01-18 14:11:23 +0100 | [diff] [blame] | 56 | sccp_to_sua(#sccp_msg{msg_type = Type, parameters = Params}) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 57 | sccp_to_sua(Type, Params). |
| 58 | sccp_to_sua(Type, Params) when Type == ?SCCP_MSGT_UDT; |
| 59 | Type == ?SCCP_MSGT_XUDT; |
| 60 | Type == ?SCCP_MSGT_LUDT -> |
Harald Welte | 8e92c9a | 2012-01-18 00:25:26 +0100 | [diff] [blame] | 61 | Opts = sccp_to_sua_params(Type, Params), |
Harald Welte | 92e783d | 2012-04-01 19:52:01 +0200 | [diff] [blame] | 62 | #xua_msg{version = 1, msg_class = ?SUA_MSGC_CL, |
Harald Welte | 8e92c9a | 2012-01-18 00:25:26 +0100 | [diff] [blame] | 63 | msg_type = ?SUA_CL_CLDT, payload = Opts}; |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 64 | sccp_to_sua(Type, Params) when Type == ?SCCP_MSGT_UDTS; |
| 65 | Type == ?SCCP_MSGT_XUDTS; |
| 66 | Type == ?SCCP_MSGT_LUDTS -> |
| 67 | Opts = sccp_to_sua_params(Params), |
Harald Welte | 92e783d | 2012-04-01 19:52:01 +0200 | [diff] [blame] | 68 | #xua_msg{version=1, msg_class = ?SUA_MSGC_CL, |
Harald Welte | 8e92c9a | 2012-01-18 00:25:26 +0100 | [diff] [blame] | 69 | msg_type = ?SUA_CL_CLDR, payload = Opts}. |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 70 | |
| 71 | |
| 72 | % CLDT parameters: |
| 73 | % ?SUA_IEI_ROUTE_CTX, ?SUA_IEI_PROTO_CLASS, ?SUA_IEI_SRC_ADDR, |
| 74 | % ?SUA_IEI_DEST_ADDR, ?SUA_IEI_SEQ_CTRL, ?SUA_IEI_S7_HOP_CTR, |
| 75 | % ?SUA_IEI_IMPORTANCE, ?SUA_IEI_MSG_PRIO, ?SUA_IEI_CORR_ID, |
| 76 | % ?SUA_IEI_SEGMENTATION, ?SUA_IEI_DATA |
| 77 | |
Harald Welte | 92e783d | 2012-04-01 19:52:01 +0200 | [diff] [blame] | 78 | sua_to_sccp_params(#xua_msg{msg_class=Class, msg_type=Type, payload=Payload}) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 79 | sua_to_sccp_params(Class, Type, Payload). |
| 80 | sua_to_sccp_params(Class, Type, Payload) -> |
| 81 | sua_to_sccp_params(Class, Type, Payload, []). |
Harald Welte | 99efaf9 | 2012-01-18 14:11:23 +0100 | [diff] [blame] | 82 | sua_to_sccp_params(_Class, _Type, [], List) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 83 | List; |
Harald Welte | 042a579 | 2012-01-18 00:09:51 +0100 | [diff] [blame] | 84 | sua_to_sccp_params(Class, Type, [{ParTag, {_Len, ParVal}}|Remain], List) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 85 | NewPars = sua_to_sccp_param(Class, Type, ParTag, ParVal), |
| 86 | sua_to_sccp_params(Class, Type, Remain, List ++ NewPars). |
| 87 | |
| 88 | % convert an individual SUA parameter to a SCCP option |
| 89 | sua_to_sccp_param(_, _, ?SUA_IEI_PROTO_CLASS, Remain) -> |
Harald Welte | 99efaf9 | 2012-01-18 14:11:23 +0100 | [diff] [blame] | 90 | <<_:24, PCOpt:4, _:2, Class:2>> = Remain, |
| 91 | [{protocol_class, {Class, PCOpt}}]; |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 92 | sua_to_sccp_param(_, _, ?SUA_IEI_SRC_ADDR, Remain) -> |
| 93 | Addr = sua_to_sccp_addr(Remain), |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 94 | [{calling_party_addr, Addr}]; |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 95 | sua_to_sccp_param(_, _, ?SUA_IEI_DEST_ADDR, Remain) -> |
| 96 | Addr = sua_to_sccp_addr(Remain), |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 97 | [{called_party_addr, Addr}]; |
Harald Welte | 99efaf9 | 2012-01-18 14:11:23 +0100 | [diff] [blame] | 98 | sua_to_sccp_param(_, _, ?SUA_IEI_SEQ_CTRL, _Remain) -> |
| 99 | % If we were to translate to a N-UNITDATA.req, we could map |
| 100 | % this, but there is no mapping to a SCCP message... |
| 101 | []; |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 102 | sua_to_sccp_param(_, _, ?SUA_IEI_S7_HOP_CTR, Remain) -> |
| 103 | <<_:24, HopCtr:8>> = Remain, |
| 104 | [{?SCCP_PNC_HOP_COUNTER, HopCtr}]; |
| 105 | sua_to_sccp_param(_, _, ?SUA_IEI_IMPORTANCE, Remain) -> |
| 106 | <<_:24, Imp:8>> = Remain, |
| 107 | [{?SCCP_PNC_IMPORTANCE, Imp}]; |
| 108 | sua_to_sccp_param(_, _, ?SUA_IEI_DATA, Remain) -> |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 109 | [{user_data, Remain}]; |
Harald Welte | 99efaf9 | 2012-01-18 14:11:23 +0100 | [diff] [blame] | 110 | sua_to_sccp_param(_, _, ?SUA_IEI_ROUTE_CTX, _Remain) -> |
Harald Welte | 042a579 | 2012-01-18 00:09:51 +0100 | [diff] [blame] | 111 | %FIXME: what to do with routing context? |
| 112 | []. |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 113 | |
| 114 | sccp_to_sua_params(#sccp_msg{msg_type=Type, parameters=Params}) -> |
| 115 | sccp_to_sua_params(Type, Params). |
| 116 | sccp_to_sua_params(Type, Params) when is_list(Params) -> |
| 117 | sccp_to_sua_params(Type, Params, []). |
Harald Welte | 99efaf9 | 2012-01-18 14:11:23 +0100 | [diff] [blame] | 118 | sccp_to_sua_params(_Type, [], List) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 119 | List; |
| 120 | sccp_to_sua_params(Type, [{ParTag, ParVal}|Tail], List) -> |
| 121 | NewPars = sccp_to_sua_param(Type, ParTag, ParVal), |
| 122 | sccp_to_sua_params(Type, Tail, List ++ NewPars). |
| 123 | |
Harald Welte | 99efaf9 | 2012-01-18 14:11:23 +0100 | [diff] [blame] | 124 | sccp_to_sua_param(_, protocol_class, {Opt, Class}) -> |
| 125 | [{?SUA_IEI_PROTO_CLASS, <<0:24, Opt:4, 0:2, Class:2>>}]; |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 126 | sccp_to_sua_param(_, calling_party_addr, Addr) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 127 | AddrSua = sccp_to_sua_addr(Addr), |
| 128 | [{?SUA_IEI_SRC_ADDR, AddrSua}]; |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 129 | sccp_to_sua_param(_, called_party_addr, Addr) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 130 | AddrSua = sccp_to_sua_addr(Addr), |
| 131 | [{?SUA_IEI_DEST_ADDR, AddrSua}]; |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 132 | sccp_to_sua_param(_, ?SCCP_PNC_HOP_COUNTER, Hop) -> |
| 133 | [{?SUA_IEI_S7_HOP_CTR, <<0:24, Hop:8>>}]; |
| 134 | sccp_to_sua_param(_, ?SCCP_PNC_IMPORTANCE, Imp) -> |
| 135 | [{?SUA_IEI_IMPORTANCE, <<0:24, Imp:8>>}]; |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 136 | sccp_to_sua_param(_, user_data, Data) -> |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 137 | [{?SUA_IEI_DATA, Data}]. |
| 138 | |
| 139 | sua_to_sccp_addr(SuaBin) -> |
| 140 | <<RoutInd:16, _:13, GTinc:1, PCinc:1, SSNinc:1, Remain/binary>> = SuaBin, |
| 141 | ParList = addr_pars_to_list(Remain), |
| 142 | case GTinc of |
| 143 | 1 -> |
Harald Welte | 042a579 | 2012-01-18 00:09:51 +0100 | [diff] [blame] | 144 | {_, GTopt} = proplists:get_value(?SUA_IEI_GT, ParList), |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 145 | GT = parse_sua_gt(GTopt); |
| 146 | 0 -> |
| 147 | GT = undefined |
| 148 | end, |
| 149 | case PCinc of |
| 150 | 1 -> |
Harald Welte | 042a579 | 2012-01-18 00:09:51 +0100 | [diff] [blame] | 151 | {_, PCopt} = proplists:get_value(?SUA_IEI_PC, ParList), |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 152 | PC = parse_sua_pc(PCopt); |
| 153 | 0 -> |
| 154 | PC = undefined |
| 155 | end, |
| 156 | case SSNinc of |
| 157 | 1 -> |
Harald Welte | 042a579 | 2012-01-18 00:09:51 +0100 | [diff] [blame] | 158 | {_, SSNopt} = proplists:get_value(?SUA_IEI_SSN, ParList), |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 159 | SSN = parse_sua_ssn(SSNopt); |
| 160 | 0 -> |
| 161 | SSN = undefined |
| 162 | end, |
| 163 | case RoutInd of |
| 164 | ?SUA_RI_GT -> |
| 165 | RoutSSN = 0; |
| 166 | ?SUA_RI_SSN_PC -> |
| 167 | RoutSSN = 1 |
| 168 | end, |
| 169 | #sccp_addr{route_on_ssn = RoutSSN, point_code = PC, ssn = SSN, global_title = GT}. |
| 170 | |
| 171 | addr_pars_to_list(Bin) -> |
Harald Welte | 92e783d | 2012-04-01 19:52:01 +0200 | [diff] [blame] | 172 | xua_codec:parse_xua_opts(Bin). |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 173 | |
| 174 | sccp_to_sua_addr(Addr) when is_record(Addr, sccp_addr) -> |
| 175 | #sccp_addr{route_on_ssn = RoutOnSsn, point_code = PC, ssn = SSN, |
| 176 | global_title = GT} = Addr, |
| 177 | case GT of |
| 178 | #global_title{} -> |
Harald Welte | 8e92c9a | 2012-01-18 00:25:26 +0100 | [diff] [blame] | 179 | GTopt = [{?SUA_IEI_GT, encode_sua_gt(GT)}], |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 180 | GTinc = 1; |
| 181 | _ -> |
| 182 | GTopt = [], |
| 183 | GTinc = 0 |
| 184 | end, |
| 185 | case PC of |
| 186 | Int when is_integer(Int) -> |
Harald Welte | 8e92c9a | 2012-01-18 00:25:26 +0100 | [diff] [blame] | 187 | PCopt = [{?SUA_IEI_PC, encode_sua_pc(PC)}], |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 188 | PCinc = 1; |
| 189 | _ -> |
| 190 | PCopt = [], |
| 191 | PCinc = 0 |
| 192 | end, |
| 193 | case SSN of |
| 194 | Int2 when is_integer(Int2) -> |
Harald Welte | 8e92c9a | 2012-01-18 00:25:26 +0100 | [diff] [blame] | 195 | SSNopt = [{?SUA_IEI_SSN, encode_sua_ssn(SSN)}], |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 196 | SSNinc = 1; |
| 197 | _ -> |
| 198 | SSNopt = [], |
| 199 | SSNinc = 0 |
| 200 | end, |
| 201 | case RoutOnSsn of |
| 202 | 0 -> |
| 203 | RoutInd = ?SUA_RI_GT; |
| 204 | 1 -> |
| 205 | RoutInd = ?SUA_RI_SSN_PC |
| 206 | end, |
Harald Welte | 92e783d | 2012-04-01 19:52:01 +0200 | [diff] [blame] | 207 | Tail = xua_codec:encode_xua_opts(GTopt ++ PCopt ++ SSNopt), |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 208 | <<RoutInd:16, 0:13, GTinc:1, PCinc:1, SSNinc:1, Tail/binary>>. |
| 209 | |
| 210 | parse_sua_gt(Bin) -> |
| 211 | <<_:24, GTI:8, NoDigits:8, TransType:8, NumPlan:8, NAI:8, Remain/binary>> = Bin, |
| 212 | Number = parse_sua_gt_digits(NoDigits, Remain), |
| 213 | #global_title{gti = GTI, nature_of_addr_ind = NAI, |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 214 | trans_type = TransType, |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 215 | numbering_plan = NumPlan, |
| 216 | phone_number = Number}. |
| 217 | encode_sua_gt(Gt) when is_record(Gt, global_title) -> |
| 218 | #global_title{gti = GTI, nature_of_addr_ind = NAI, |
Harald Welte | 7e1c261 | 2012-01-18 07:44:31 +0100 | [diff] [blame] | 219 | trans_type = TransType, |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 220 | numbering_plan = NumPlan, |
| 221 | phone_number = Number} = Gt, |
| 222 | NoDigits = count_digits(Number), |
| 223 | DigitBin = encode_sua_gt_digits(Number), |
| 224 | <<0:24, GTI:8, NoDigits:8, TransType:8, NumPlan:8, NAI:8, DigitBin/binary>>. |
| 225 | |
| 226 | count_digits(Number) when is_integer(Number) -> |
| 227 | BcdList = osmo_util:int2digit_list(Number), |
| 228 | count_digits(BcdList); |
| 229 | count_digits(Number) when is_list(Number) -> |
| 230 | length(Number). |
| 231 | |
| 232 | |
| 233 | parse_sua_gt_digits(NoDigits, Remain) -> |
| 234 | % as opposed to ISUP/SCCP, we can have more than one nibble padding, |
| 235 | OddEven = NoDigits rem 1, |
| 236 | case OddEven of |
| 237 | 0 -> |
Harald Welte | 042a579 | 2012-01-18 00:09:51 +0100 | [diff] [blame] | 238 | ByteLen = NoDigits div 2; |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 239 | 1 -> |
Harald Welte | 042a579 | 2012-01-18 00:09:51 +0100 | [diff] [blame] | 240 | ByteLen = NoDigits div 2 + 1 |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 241 | end, |
| 242 | <<Bin:ByteLen/binary, _/binary>> = Remain, |
| 243 | isup_codec:parse_isup_party(Bin, OddEven). |
| 244 | encode_sua_gt_digits(Digits) when is_list(Digits); is_integer(Digits) -> |
| 245 | % Assume that overall option encoder will do the padding... |
Harald Welte | 8e92c9a | 2012-01-18 00:25:26 +0100 | [diff] [blame] | 246 | {Enc, _OddEven} = isup_codec:encode_isup_party(Digits), |
| 247 | Enc. |
Harald Welte | 50dfc19 | 2012-01-17 15:11:37 +0100 | [diff] [blame] | 248 | |
| 249 | parse_sua_pc(<<PC:32/big>>) -> |
| 250 | PC. |
| 251 | encode_sua_pc(Pc) when is_integer(Pc) -> |
| 252 | <<Pc:32/big>>. |
| 253 | |
| 254 | parse_sua_ssn(<<_:24, SSN:8>>) -> |
| 255 | SSN. |
| 256 | encode_sua_ssn(Ssn) when is_integer(Ssn) -> |
| 257 | <<0:24, Ssn:8>>. |