blob: 03df3d8702e98e91c96822f747d9853e6cf27ada [file] [log] [blame]
Andrey Velikiyb39df232019-06-14 21:08:00 +03001% This Source Code Form is subject to the terms of the Mozilla Public
2% License, v. 2.0. If a copy of the MPL was not distributed with this
3% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4% (C) 2019 Andrey Velikiy <agreat22@gmail.com>
5% (C) 2019 Fairwaves (edited)
6
7-module(gsup_protocol).
8
9-include ("gsup_protocol.hrl").
10-include ("ipa.hrl").
11
12-export([decode/1, encode/1, decode_bcd/1, encode_bcd/1]).
13-export_type(['GSUPMessage'/0, 'GSUPMessageType'/0]).
14
15-define (CHECK_SIZE(IE, Len, Value),
16 Value >= 0 andalso Value < (1 bsl (Len * 8)) orelse error({ie_value_length_mismatch, IE, Value})
17 ).
18
19-define (CHECK_LEN(IE, Len, Min, Max),
20 Len >= Min andalso Len =< Max orelse error({ie_length_mismatch, IE, Len})
21 ).
22
23-ifdef (TEST).
24-export ([encode_ie/2, decode_ie/2]).
25-endif.
26
27-spec decode(binary()) -> 'GSUPMessage'() | no_return().
28decode(<<MsgType, Tail/binary>>) ->
29 case ?GSUP_MESSAGES() of
30 #{MsgType := #{message_type := MsgTypeAtom, mandatory := Mandatory0}} ->
31 GSUPMessage = decode_ie(Tail, #{message_type => MsgTypeAtom}),
32 Mandatory = Mandatory0 ++ ?MANDATORY_DEFAULT,
33 case maps:size(maps:with(Mandatory, GSUPMessage)) == length(Mandatory) of
34 true -> GSUPMessage;
35 false -> error({mandatory_ie_missing, MsgTypeAtom, Mandatory -- maps:keys(GSUPMessage)})
36 end;
37 _ ->
38 error({unknown_gsup_msg_type, MsgType})
39 end.
40
41decode_ie(<<>>, Map) -> Map;
42
43decode_ie(<<?IMSI, Len, IMSI:Len/binary, Tail/binary>>, Map) ->
44 ?CHECK_LEN(imsi, Len, 0, 8),
45 decode_ie(Tail, Map#{imsi => decode_bcd(IMSI, <<>>)});
46
47decode_ie(<<?CAUSE, Len, Cause:Len/unit:8, Tail/binary>>, Map) ->
48 ?CHECK_LEN(cause, Len, 1, 1),
49 decode_ie(Tail, Map#{cause => Cause});
50
51decode_ie(<<?AUTH_TUPLE, Len, AuthTuple0:Len/binary, Tail/binary>>, Map) ->
52 List = maps:get(auth_tuples, Map, []),
53 ?CHECK_LEN(auth_tuples, length(List) + 1, 1, 5),
54 AuthTuple = decode_auth_tuple(AuthTuple0, #{}),
55 check_auth_tuple(AuthTuple) orelse error({bad_auth_tuple, AuthTuple}),
56 decode_ie(Tail, Map#{auth_tuples => List ++ [AuthTuple]});
57
58decode_ie(<<?PDP_INFO_COMPLETE, Len, _:Len/binary, Tail/binary>>, Map) ->
59 ?CHECK_LEN(pdp_info_complete, Len, 0, 0),
60 decode_ie(Tail, Map#{pdp_info_complete => true});
61
62decode_ie(<<?PDP_INFO, Len, PDPInfo0:Len/binary, Tail/binary>>, Map) ->
63 List = maps:get(pdp_info_list, Map, []),
64 ?CHECK_LEN(pdp_info_list, length(List) + 1, 1, 10),
65 PDPInfo = decode_pdp_info(PDPInfo0, #{}),
66 check_pdp_info(PDPInfo) orelse error({bad_pdp_info, PDPInfo}),
67 decode_ie(Tail, Map#{pdp_info_list => List ++ [PDPInfo]});
68
69decode_ie(<<?CANCELLATION_TYPE, Len, CancellationType:Len/unit:8, Tail/binary>>, Map) ->
70 ?CHECK_LEN(cancellation_type, Len, 1, 1),
71 decode_ie(Tail, Map#{cancellation_type => CancellationType});
72
73decode_ie(<<?FREEZE_P_TMSI, Len, _:Len/binary, Tail/binary>>, Map) ->
74 ?CHECK_LEN(freeze_p_tmsi, Len, 0, 0),
75 decode_ie(Tail, Map#{freeze_p_tmsi => true});
76
77decode_ie(<<?MSISDN, Len, MSISDN:Len/binary, Tail/binary>>, Map) ->
78 ?CHECK_LEN(msisdn, Len, 0, 8),
79 decode_ie(Tail, Map#{msisdn => MSISDN});
80
81decode_ie(<<?HLR_NUMBER, Len, HLRNumber:Len/binary, Tail/binary>>, Map) ->
82 ?CHECK_LEN(hlr_number, Len, 0, 8),
83 decode_ie(Tail, Map#{hlr_number => HLRNumber});
84
Andrey Velikiyc66c8d02019-06-19 14:16:52 +030085decode_ie(<<?MESSAGE_CLASS, Len, MessageClass:Len/unit:8, Tail/binary>>, Map) ->
86 ?CHECK_LEN(message_class, Len, 1, 1),
87 decode_ie(Tail, Map#{message_class => MessageClass});
88
Andrey Velikiyb39df232019-06-14 21:08:00 +030089decode_ie(<<?PDP_CONTEXT_ID, Len, PDPContextId:Len/unit:8, Tail/binary>>, Map) ->
90 ?CHECK_LEN(pdp_context_id, Len, 1, 1),
91 List = maps:get(pdp_context_id, Map, []),
92 ?CHECK_LEN(pdp_context_id_list, length(List) + 1, 1, 10),
93 decode_ie(Tail, Map#{pdp_context_id => List ++ [PDPContextId]});
94
95decode_ie(<<?PDP_CHARGING, Len, PDPCharging:Len/unit:8, Tail/binary>>, Map) ->
96 ?CHECK_LEN(pdp_charging, Len, 2, 2),
97 decode_ie(Tail, Map#{pdp_charging => PDPCharging});
98
Andrey Velikiy1e801782019-06-24 14:09:57 +030099decode_ie(<<?RAND, Len, Rand:Len/binary, Tail/binary>>, Map) ->
Andrey Velikiyb39df232019-06-14 21:08:00 +0300100 ?CHECK_LEN(rand, Len, 16, 16),
101 decode_ie(Tail, Map#{rand => Rand});
102
Andrey Velikiy1e801782019-06-24 14:09:57 +0300103decode_ie(<<?AUTS, Len, AUTS:Len/binary, Tail/binary>>, Map) ->
Andrey Velikiyb39df232019-06-14 21:08:00 +0300104 ?CHECK_LEN(auts, Len, 14, 14),
105 decode_ie(Tail, Map#{auts => AUTS});
106
107decode_ie(<<?CN_DOMAIN, Len, CN_Domain:Len/unit:8, Tail/binary>>, Map) ->
108 ?CHECK_LEN(cn_domain, Len, 1, 1),
109 decode_ie(Tail, Map#{cn_domain => CN_Domain});
110
Harald Welteb76c7fa2019-08-22 13:48:00 +0200111decode_ie(<<?RAT_TYPE, Len, Rat_Type:Len/binary, Tail/binary>>, Map) ->
112 ?CHECK_LEN(rat_type, Len, 1, 8),
113 decode_ie(Tail, Map#{rat_type => decode_rat_types(binary_to_list(Rat_Type))});
114
Andrey Velikiyb39df232019-06-14 21:08:00 +0300115decode_ie(<<?SESSION_ID, Len, SesID:Len/unit:8, Tail/binary>>, Map) ->
116 ?CHECK_LEN(session_id, Len, 4, 4),
117 decode_ie(Tail, Map#{session_id => SesID});
118
119decode_ie(<<?SESSION_STATE, Len, SesState:Len/unit:8, Tail/binary>>, Map) ->
120 ?CHECK_LEN(session_state, Len, 1, 1),
121 decode_ie(Tail, Map#{session_state => SesState});
122
123decode_ie(<<?SS_INFO, Len, SesInfo:Len/binary, Tail/binary>>, Map) ->
124 decode_ie(Tail, Map#{ss_info => SesInfo});
125
126decode_ie(<<?SM_RP_MR, Len, MsgRef:Len/unit:8, Tail/binary>>, Map) ->
127 ?CHECK_LEN(sm_rp_mr, Len, 1, 1),
128 decode_ie(Tail, Map#{sm_rp_mr => MsgRef});
129
130decode_ie(<<?SM_RP_DA, Len, DA:Len/binary, Tail/binary>>, Map) ->
131 decode_ie(Tail, Map#{sm_rp_da => DA});
132
133decode_ie(<<?SM_RP_OA, Len, OA:Len/binary, Tail/binary>>, Map) ->
134 decode_ie(Tail, Map#{sm_rp_oa => OA});
135
136decode_ie(<<?SM_RP_UI, Len, MessageBody:Len/binary, Tail/binary>>, Map) ->
137 decode_ie(Tail, Map#{sm_rp_ui => MessageBody});
138
139decode_ie(<<?SM_RP_CAUSE, Len, RPCause:Len/unit:8, Tail/binary>>, Map) ->
140 ?CHECK_LEN(sm_rp_cause, Len, 1, 1),
141 decode_ie(Tail, Map#{sm_rp_cause => RPCause});
142
143decode_ie(<<?SM_RP_MMS, Len, RPMMS:Len/unit:8, Tail/binary>>, Map) ->
144 ?CHECK_LEN(sm_rp_mms, Len, 1, 1),
145 decode_ie(Tail, Map#{sm_rp_mms => RPMMS});
146
147decode_ie(<<?SM_ALERT_REASON, Len, AlertReason:Len/unit:8, Tail/binary>>, Map) ->
148 decode_ie(Tail, Map#{sm_alert_reason => AlertReason});
149
150decode_ie(<<?IMEI, Len, IMEI:Len/binary, Tail/binary>>, Map) ->
151 ?CHECK_LEN(imei, Len, 9, 9),
152 decode_ie(Tail, Map#{imei => IMEI});
153
154decode_ie(<<?IMEI_CHECK_RESULT, Len, IMEIResult:Len/unit:8, Tail/binary>>, Map) ->
155 ?CHECK_LEN(imei_check_result, Len, 1, 1),
156 decode_ie(Tail, Map#{imei_check_result => IMEIResult});
157
Andrey Velikiybb299662019-06-19 14:19:10 +0300158decode_ie(<<?SOURCE_NAME, Len, SourceName:Len/binary, Tail/binary>>, Map) ->
159 decode_ie(Tail, Map#{source_name => SourceName});
160
161decode_ie(<<?DESTINATION_NAME, Len, DestName:Len/binary, Tail/binary>>, Map) ->
162 decode_ie(Tail, Map#{destination_name => DestName});
163
164decode_ie(<<?AN_APDU, Len, AN_APDU:Len/binary, Tail/binary>>, Map) ->
165 decode_ie(Tail, Map#{an_apdu => AN_APDU});
166
167decode_ie(<<?RR_CAUSE, Len, RRCause:Len/unit:8, Tail/binary>>, Map) ->
168 ?CHECK_LEN(rr_cause, Len, 1, 1),
169 decode_ie(Tail, Map#{rr_cause => RRCause});
170
171decode_ie(<<?BSSAP_CAUSE, Len, BSSAPCause:Len/unit:8, Tail/binary>>, Map) ->
172 ?CHECK_LEN(bssap_cause, Len, 1, 1),
173 decode_ie(Tail, Map#{bssap_cause => BSSAPCause});
174
175decode_ie(<<?SESSION_MANAGEMENT_CAUSE, Len, SMCause:Len/unit:8, Tail/binary>>, Map) ->
176 ?CHECK_LEN(session_management_cause, Len, 1, 1),
177 decode_ie(Tail, Map#{session_management_cause => SMCause});
178
Andrey Velikiyb39df232019-06-14 21:08:00 +0300179decode_ie(<<_, Len, _:Len/binary, Tail/binary>>, Map) -> %% skip unknown IE
180 decode_ie(Tail, Map);
181
182decode_ie(X, Map) ->
183 error({cannot_decode_ie, X, Map}).
184
185-spec decode_bcd(binary()) -> binary().
186decode_bcd(BCDNumber) -> decode_bcd(BCDNumber, <<>>).
187
188decode_bcd(<<>>, Number) -> Number;
189
190decode_bcd(<<A:4, B:4, Tail/binary>>, Number) when A < 15, B < 15 ->
191 decode_bcd(Tail, <<Number/binary, (decode_nibble(B)), (decode_nibble(A))>>);
192
193decode_bcd(<<_:4, B:4, _Tail/binary>>, Number) when B < 15 ->
194 <<Number/binary, (decode_nibble(B))>>.
195
196decode_nibble(0) -> $0;
197decode_nibble(1) -> $1;
198decode_nibble(2) -> $2;
199decode_nibble(3) -> $3;
200decode_nibble(4) -> $4;
201decode_nibble(5) -> $5;
202decode_nibble(6) -> $6;
203decode_nibble(7) -> $7;
204decode_nibble(8) -> $8;
205decode_nibble(9) -> $9;
206decode_nibble(10) -> $*;
207decode_nibble(11) -> $#;
208decode_nibble(12) -> $a;
209decode_nibble(13) -> $b;
210decode_nibble(14) -> $c.
211
212decode_auth_tuple(<<?RAND, Len, Rand:Len/binary, Tail/binary>>, Map) ->
213 ?CHECK_LEN(rand, Len, 16, 16),
214 decode_auth_tuple(Tail, Map#{rand => Rand});
215
216decode_auth_tuple(<<?SRES, Len, SRES:Len/binary, Tail/binary>>, Map) ->
217 ?CHECK_LEN(sres, Len, 4, 4),
218 decode_auth_tuple(Tail, Map#{sres => SRES});
219
220decode_auth_tuple(<<?KC, Len, KC:Len/binary, Tail/binary>>, Map) ->
221 ?CHECK_LEN(kc, Len, 8, 8),
222 decode_auth_tuple(Tail, Map#{kc => KC});
223
224decode_auth_tuple(<<?IK, Len, IK:Len/binary, Tail/binary>>, Map) ->
225 ?CHECK_LEN(ik, Len, 16, 16),
226 decode_auth_tuple(Tail, Map#{ik => IK});
227
228decode_auth_tuple(<<?CK, Len, CK:Len/binary, Tail/binary>>, Map) ->
229 ?CHECK_LEN(ck, Len, 16, 16),
230 decode_auth_tuple(Tail, Map#{ck => CK});
231
232decode_auth_tuple(<<?AUTN, Len, AUTN:Len/binary, Tail/binary>>, Map) ->
233 ?CHECK_LEN(autn, Len, 16, 16),
234 decode_auth_tuple(Tail, Map#{autn => AUTN});
235
236decode_auth_tuple(<<?RES, Len, Res:Len/binary, Tail/binary>>, Map) ->
237 ?CHECK_LEN(res, Len, 0, 16),
238 decode_auth_tuple(Tail, Map#{res => Res});
239
240decode_auth_tuple(<<>>, Map) -> Map.
241
242decode_pdp_info(<<?PDP_CONTEXT_ID, Len, PDPContextId:Len/unit:8, Tail/binary>>, Map) ->
243 ?CHECK_LEN(pdp_context_id, Len, 1, 1),
244 decode_pdp_info(Tail, Map#{pdp_context_id => PDPContextId});
245
246decode_pdp_info(<<?PDP_TYPE, Len, PDPType:Len/unit:8, Tail/binary>>, Map) ->
247 ?CHECK_LEN(pdp_type, Len, 2, 2),
248 decode_pdp_info(Tail, Map#{pdp_type => PDPType});
249
250decode_pdp_info(<<?ACCESS_POINT_NAME, Len, APN:Len/binary, Tail/binary>>, Map) ->
251 ?CHECK_LEN(access_point_name, Len, 1, 100),
252 decode_pdp_info(Tail, Map#{access_point_name => APN});
253
254decode_pdp_info(<<?QUALITY_OF_SERVICE, Len, QOS:Len/binary, Tail/binary>>, Map) ->
255 ?CHECK_LEN(quality_of_service, Len, 1, 20),
256 decode_pdp_info(Tail, Map#{quality_of_service => QOS});
257
258decode_pdp_info(<<?PDP_CHARGING, Len, PDPCharging:Len/unit:8, Tail/binary>>, Map) ->
259 ?CHECK_LEN(pdp_charging, Len, 2, 2),
260 decode_pdp_info(Tail, Map#{pdp_charging => PDPCharging});
261
262decode_pdp_info(<<>>, Map) -> Map.
263
Harald Welteb76c7fa2019-08-22 13:48:00 +0200264decode_rat_type(0) -> rat_unknown;
265decode_rat_type(1) -> rat_geran_a;
266decode_rat_type(2) -> rat_utran_iu;
267decode_rat_type(3) -> rat_eutran_sgs.
268
269decode_rat_types([], Acc) -> lists:reverse(Acc);
270decode_rat_types([Head|Tail], Acc) ->
271 T = decode_rat_type(Head),
272 decode_rat_types(Tail, [T|Acc]).
273decode_rat_types(List) -> decode_rat_types(List, []).
274
Andrey Velikiyb39df232019-06-14 21:08:00 +0300275-spec encode('GSUPMessage'()) -> binary() | no_return().
276encode(GSUPMessage = #{message_type := MsgTypeAtom}) when is_atom(MsgTypeAtom) ->
277 F = fun
278 (MsgType_, #{message_type := MsgTypeAtom_}, undefined) when MsgTypeAtom_ == MsgTypeAtom -> MsgType_;
279 (_, _, Acc) -> Acc
280 end,
281 case maps:fold(F, undefined, ?GSUP_MESSAGES()) of
282 undefined -> error({unknown_message_type, MsgTypeAtom}), MsgType = undefined;
283 MsgType when is_integer(MsgType) -> ok
284 end,
285 encode(MsgType, GSUPMessage).
286
287encode(MsgType, GSUPMessage) when is_integer(MsgType), is_map(GSUPMessage), MsgType >=0, MsgType =< 255 ->
288 case ?GSUP_MESSAGES() of
289 #{MsgType := #{message_type := MsgTypeAtom, mandatory := Mandatory0} = Map} ->
290 Mandatory = Mandatory0 ++ ?MANDATORY_DEFAULT,
Andrey Velikiyc66c8d02019-06-19 14:16:52 +0300291 Possible = Mandatory ++ maps:get(optional, Map, []) ++ ?OPTIONAL_DEFAULT,
Andrey Velikiyb39df232019-06-14 21:08:00 +0300292 case {maps:size(maps:with(Mandatory, GSUPMessage)) == length(Mandatory),
293 maps:size(maps:without(Possible, GSUPMessage)) == 0} of
294 {true, true} ->
295 Tail = encode_ie(GSUPMessage, <<>>),
296 <<MsgType, Tail/binary>>;
297 {false, _} -> error({mandatory_ie_missing, MsgTypeAtom, Mandatory -- maps:keys(GSUPMessage)});
298 {_, false} -> error({ie_not_expected, MsgTypeAtom, maps:keys(GSUPMessage) -- Possible})
299 end;
300 _ ->
301 error({unknown_gsup_msg_type, MsgType})
302 end.
303
304encode_ie(#{imsi := Value0} = GSUPMessage, Head) ->
305 Value = encode_bcd(Value0, <<>>),
306 Len = size(Value),
307 ?CHECK_LEN(imsi, Len, 0, 8),
308 encode_ie(maps:without([imsi], GSUPMessage), <<Head/binary, ?IMSI, Len, Value/binary>>);
309
310encode_ie(#{cause := Value} = GSUPMessage, Head) ->
311 Len = 1,
312 ?CHECK_SIZE(cause, Len, Value),
313 encode_ie(maps:without([cause], GSUPMessage), <<Head/binary, ?CAUSE, Len, Value:Len/unit:8>>);
314
315encode_ie(#{auth_tuples := Tuples0} = GSUPMessage, Head) ->
316 ?CHECK_LEN(auth_tuples, length(Tuples0), 0, 5),
317 Tuples = <<
318 begin
319 check_auth_tuple(Tuple) orelse error({bad_auth_tuple, Tuple}),
320 Value = encode_auth_tuple(Tuple, <<>>),
321 Len = size(Value),
322 <<?AUTH_TUPLE, Len, Value/binary>>
323 end || Tuple <- Tuples0>>,
324 encode_ie(maps:without([auth_tuples], GSUPMessage), <<Head/binary, Tuples/binary>>);
325
Andrey Velikiy1e801782019-06-24 14:09:57 +0300326encode_ie(#{msisdn := Value} = GSUPMessage, Head) ->
327 Len = size(Value),
328 ?CHECK_LEN(msisdn, Len, 0, 8),
329 encode_ie(maps:without([msisdn], GSUPMessage), <<Head/binary, ?MSISDN, Len, Value/binary>>);
330
331encode_ie(#{hlr_number := Value} = GSUPMessage, Head) ->
332 Len = size(Value),
333 ?CHECK_LEN(hlr_number, Len, 0, 8),
334 encode_ie(maps:without([hlr_number], GSUPMessage), <<Head/binary, ?HLR_NUMBER, Len, Value/binary>>);
335
Andrey Velikiyb39df232019-06-14 21:08:00 +0300336encode_ie(#{pdp_info_complete := true} = GSUPMessage, Head) ->
337 encode_ie(maps:without([pdp_info_complete], GSUPMessage), <<Head/binary, ?PDP_INFO_COMPLETE, 0>>);
338
339encode_ie(#{pdp_info_complete := _} = _GSUPMessage, _Head) ->
340 error(pdp_info_complete_must_be_true);
341
342encode_ie(#{pdp_info_list := PDPInfoList0} = GSUPMessage, Head) -> %% PDPInfo
343 ?CHECK_LEN(pdp_info_list, length(PDPInfoList0), 0, 10),
344 PDPInfoList = <<
345 begin
346 check_pdp_info(PDPInfo) orelse error({bad_pdp_info, PDPInfo}),
347 Value = encode_pdp_info(PDPInfo, <<>>),
348 Len = size(Value),
349 <<?PDP_INFO, Len, Value/binary>>
350 end || PDPInfo <- PDPInfoList0>>,
351 encode_ie(maps:without([pdp_info_list], GSUPMessage), <<Head/binary, PDPInfoList/binary>>);
352
353encode_ie(#{cancellation_type := Value} = GSUPMessage, Head) ->
354 Len = 1,
355 ?CHECK_SIZE(cancellation_type, Len, Value),
356 encode_ie(maps:without([cancellation_type], GSUPMessage), <<Head/binary, ?CANCELLATION_TYPE, Len, Value:Len/unit:8>>);
357
358encode_ie(#{freeze_p_tmsi := true} = GSUPMessage, Head) ->
359 encode_ie(maps:without([freeze_p_tmsi], GSUPMessage), <<Head/binary, ?FREEZE_P_TMSI, 0>>);
360
361encode_ie(#{freeze_p_tmsi := _} = _GSUPMessage, _Head) ->
362 error(freeze_p_tmsi_must_be_true);
363
Andrey Velikiy1e801782019-06-24 14:09:57 +0300364encode_ie(#{session_id := Value} = GSUPMessage, Head) ->
365 Len = 4,
366 ?CHECK_SIZE(session_id, Len, Value),
367 encode_ie(maps:without([session_id], GSUPMessage), <<Head/binary, ?SESSION_ID, Len, Value:Len/unit:8>>);
Andrey Velikiyb39df232019-06-14 21:08:00 +0300368
Andrey Velikiy1e801782019-06-24 14:09:57 +0300369encode_ie(#{session_state := Value} = GSUPMessage, Head) ->
370 Len = 1,
371 ?CHECK_SIZE(session_state, Len, Value),
372 encode_ie(maps:without([session_state], GSUPMessage), <<Head/binary, ?SESSION_STATE, Len, Value:Len/unit:8>>);
Andrey Velikiyb39df232019-06-14 21:08:00 +0300373
Andrey Velikiyc66c8d02019-06-19 14:16:52 +0300374encode_ie(#{message_class := Value} = GSUPMessage, Head) ->
375 Len = 1,
376 ?CHECK_SIZE(message_class, Len, Value),
377 encode_ie(maps:without([message_class], GSUPMessage), <<Head/binary, ?MESSAGE_CLASS, Len, Value:Len/unit:8>>);
378
Andrey Velikiyb39df232019-06-14 21:08:00 +0300379encode_ie(#{pdp_context_id := PDPCIdList0} = GSUPMessage, Head) ->
380 Len = 1,
381 PDPCIdList = <<
382 begin
383 ?CHECK_SIZE(pdp_context_id, Len, Value),
384 <<?PDP_CONTEXT_ID, Len, Value:Len/unit:8>>
385 end || Value <- PDPCIdList0>>,
386 encode_ie(maps:without([pdp_context_id], GSUPMessage), <<Head/binary, PDPCIdList/binary>>);
387
388encode_ie(#{pdp_charging := Value} = GSUPMessage, Head) ->
389 Len = 2,
390 ?CHECK_SIZE(pdp_charging, Len, Value),
391 encode_ie(maps:without([pdp_charging], GSUPMessage), <<Head/binary, ?PDP_CHARGING, Len, Value:Len/unit:8>>);
392
Andrey Velikiyb39df232019-06-14 21:08:00 +0300393encode_ie(#{auts := Value} = GSUPMessage, Head) ->
394 Len = 14,
395 ?CHECK_LEN(auts, size(Value), Len, Len),
Andrey Velikiy1e801782019-06-24 14:09:57 +0300396 encode_ie(maps:without([auts], GSUPMessage), <<Head/binary, ?AUTS, Len, Value:Len/binary>>);
397
398encode_ie(#{rand := Value} = GSUPMessage, Head) ->
399 Len = 16,
400 ?CHECK_LEN(rand, size(Value), Len, Len),
401 encode_ie(maps:without([rand], GSUPMessage), <<Head/binary, ?RAND, Len, Value:Len/binary>>);
Andrey Velikiyb39df232019-06-14 21:08:00 +0300402
403encode_ie(#{cn_domain := Value} = GSUPMessage, Head) ->
404 Len = 1,
405 ?CHECK_SIZE(cn_domain, Len, Value),
406 encode_ie(maps:without([cn_domain], GSUPMessage), <<Head/binary, ?CN_DOMAIN, Len, Value:Len/unit:8>>);
407
Harald Welteb76c7fa2019-08-22 13:48:00 +0200408encode_ie(#{rat_type := Value} = GSUPMessage, Head) when is_list(Value) ->
409 Len = length(Value),
410 ?CHECK_LEN(rat_type, Len, 1, 8),
411 RatList = encode_rat_types(Value),
412 encode_ie(maps:without([rat_type], GSUPMessage), <<Head/binary, ?RAT_TYPE, Len, RatList/binary>>);
413
Andrey Velikiyb39df232019-06-14 21:08:00 +0300414encode_ie(#{ss_info := Value} = GSUPMessage, Head) ->
415 Len = size(Value),
416 encode_ie(maps:without([ss_info], GSUPMessage), <<Head/binary, ?SS_INFO, Len, Value/binary>>);
417
418encode_ie(#{sm_rp_mr := Value} = GSUPMessage, Head) ->
419 Len = 1,
420 ?CHECK_SIZE(sm_rp_mr, Len, Value),
421 encode_ie(maps:without([sm_rp_mr], GSUPMessage), <<Head/binary, ?SM_RP_MR, Len, Value:Len/unit:8>>);
422
423encode_ie(#{sm_rp_da := Value} = GSUPMessage, Head) ->
424 Len = size(Value),
425 encode_ie(maps:without([sm_rp_da], GSUPMessage), <<Head/binary, ?SM_RP_DA, Len, Value/binary>>);
426
427encode_ie(#{sm_rp_oa := Value} = GSUPMessage, Head) ->
428 Len = size(Value),
429 encode_ie(maps:without([sm_rp_oa], GSUPMessage), <<Head/binary, ?SM_RP_OA, Len, Value/binary>>);
430
431encode_ie(#{sm_rp_ui := Value} = GSUPMessage, Head) ->
432 Len = size(Value),
433 encode_ie(maps:without([sm_rp_ui], GSUPMessage), <<Head/binary, ?SM_RP_UI, Len, Value/binary>>);
434
435encode_ie(#{sm_rp_cause := Value} = GSUPMessage, Head) ->
436 Len = 1,
437 ?CHECK_SIZE(sm_rp_cause, Len, Value),
438 encode_ie(maps:without([sm_rp_cause], GSUPMessage), <<Head/binary, ?SM_RP_CAUSE, Len, Value:Len/unit:8>>);
439
440encode_ie(#{sm_rp_mms := Value} = GSUPMessage, Head) ->
441 Len = 1,
442 ?CHECK_SIZE(sm_rp_mms, Len, Value),
443 encode_ie(maps:without([sm_rp_mms], GSUPMessage), <<Head/binary, ?SM_RP_MMS, Len, Value:Len/unit:8>>);
444
445encode_ie(#{sm_alert_reason := Value} = GSUPMessage, Head) ->
446 Len = 1,
447 ?CHECK_SIZE(sm_alert_reason, Len, Value),
448 encode_ie(maps:without([sm_alert_reason], GSUPMessage), <<Head/binary, ?SM_ALERT_REASON, Len, Value:Len/unit:8>>);
449
450encode_ie(#{imei := Value} = GSUPMessage, Head) ->
451 Len = size(Value),
Andrey Velikiy1e801782019-06-24 14:09:57 +0300452 ?CHECK_LEN(imei, Len, 9, 9),
Andrey Velikiyb39df232019-06-14 21:08:00 +0300453 encode_ie(maps:without([imei], GSUPMessage), <<Head/binary, ?IMEI, Len, Value/binary>>);
454
455encode_ie(#{imei_check_result := Value} = GSUPMessage, Head) ->
456 Len = 1,
457 ?CHECK_SIZE(imei_check_result, Len, Value),
458 encode_ie(maps:without([imei_check_result], GSUPMessage), <<Head/binary, ?IMEI_CHECK_RESULT, Len, Value:Len/unit:8>>);
459
Andrey Velikiybb299662019-06-19 14:19:10 +0300460encode_ie(#{source_name := Value} = GSUPMessage, Head) ->
461 Len = size(Value),
462 encode_ie(maps:without([source_name], GSUPMessage), <<Head/binary, ?SOURCE_NAME, Len, Value/binary>>);
463
464encode_ie(#{destination_name := Value} = GSUPMessage, Head) ->
465 Len = size(Value),
466 encode_ie(maps:without([destination_name], GSUPMessage), <<Head/binary, ?DESTINATION_NAME, Len, Value/binary>>);
467
468encode_ie(#{an_apdu := Value} = GSUPMessage, Head) ->
469 Len = size(Value),
470 encode_ie(maps:without([an_apdu], GSUPMessage), <<Head/binary, ?AN_APDU, Len, Value/binary>>);
471
472encode_ie(#{rr_cause := Value} = GSUPMessage, Head) ->
473 Len = 1,
474 ?CHECK_SIZE(rr_cause, Len, Value),
475 encode_ie(maps:without([rr_cause], GSUPMessage), <<Head/binary, ?RR_CAUSE, Len, Value:Len/unit:8>>);
476
477encode_ie(#{bssap_cause := Value} = GSUPMessage, Head) ->
478 Len = 1,
479 ?CHECK_SIZE(bssap_cause, Len, Value),
480 encode_ie(maps:without([bssap_cause], GSUPMessage), <<Head/binary, ?BSSAP_CAUSE, Len, Value:Len/unit:8>>);
481
482encode_ie(#{session_management_cause := Value} = GSUPMessage, Head) ->
483 Len = 1,
484 ?CHECK_SIZE(session_management_cause, Len, Value),
485 encode_ie(maps:without([session_management_cause], GSUPMessage), <<Head/binary, ?SESSION_MANAGEMENT_CAUSE, Len, Value:Len/unit:8>>);
486
Andrey Velikiyb39df232019-06-14 21:08:00 +0300487encode_ie(_, Head) -> Head.
488
489encode_bcd(BCDNumber) -> encode_bcd(BCDNumber, <<>>).
490
491encode_bcd(<<A, B, Tail/binary>>, BCDNumber) ->
492 encode_bcd(Tail, <<BCDNumber/binary, (encode_nibble(B)):4, (encode_nibble(A)):4>>);
493
494encode_bcd(<<A>>, BCDNumber) ->
495 <<BCDNumber/binary, 16#f:4, (encode_nibble(A)):4>>;
496
497encode_bcd(<<>>, BCDNumber) ->
498 BCDNumber.
499
500encode_nibble($0) -> 0;
501encode_nibble($1) -> 1;
502encode_nibble($2) -> 2;
503encode_nibble($3) -> 3;
504encode_nibble($4) -> 4;
505encode_nibble($5) -> 5;
506encode_nibble($6) -> 6;
507encode_nibble($7) -> 7;
508encode_nibble($8) -> 8;
509encode_nibble($9) -> 9;
510encode_nibble($*) -> 10;
511encode_nibble($#) -> 11;
512encode_nibble($a) -> 12;
513encode_nibble($b) -> 13;
514encode_nibble($c) -> 14;
515encode_nibble($A) -> 12;
516encode_nibble($B) -> 13;
517encode_nibble($C) -> 14;
518encode_nibble(X) -> error({bad_bcd_character, X}).
519
520check_auth_tuple(AuthTuple) ->
521 Mandatory = ?AUTH_TUPLE_MANDATORY,
522 Possible = Mandatory ++ ?AUTH_TUPLE_OPTIONAL,
523 (maps:size(maps:with(Mandatory, AuthTuple)) == length(Mandatory))
524 orelse error({mandatory_ie_missing, auth_tuples, Mandatory -- maps:keys(AuthTuple)}),
525 (maps:size(maps:without(Possible, AuthTuple)) == 0)
526 orelse error({ie_not_expected, auth_tuples, maps:keys(AuthTuple) -- Possible}).
527
528check_pdp_info(PDPInfo) ->
529 Mandatory = ?PDP_INFO_MANDATORY,
530 Possible = Mandatory ++ ?PDP_INFO_OPTIONAL,
531 (maps:size(maps:with(Mandatory, PDPInfo)) == length(Mandatory))
532 orelse error({mandatory_ie_missing, pdp_info_list, Mandatory -- maps:keys(PDPInfo)}),
533 (maps:size(maps:without(Possible, PDPInfo)) == 0)
534 orelse error({ie_not_expected, pdp_info_list, maps:keys(PDPInfo) -- Possible}).
535
536encode_auth_tuple(#{rand := Value} = Map, Head) ->
537 Len = 16,
538 ?CHECK_LEN(rand, size(Value), Len, Len),
539 encode_auth_tuple(maps:without([rand], Map), <<Head/binary, ?RAND, Len, Value:Len/binary>>);
540
541encode_auth_tuple(#{sres := Value} = Map, Head) ->
542 Len = 4,
543 ?CHECK_LEN(sres, size(Value), Len, Len),
544 encode_auth_tuple(maps:without([sres], Map), <<Head/binary, ?SRES, Len, Value:Len/binary>>);
545
546encode_auth_tuple(#{kc := Value} = Map, Head) ->
547 Len = 8,
548 ?CHECK_LEN(kc, size(Value), Len, Len),
549 encode_auth_tuple(maps:without([kc], Map), <<Head/binary, ?KC, Len, Value:Len/binary>>);
550
551encode_auth_tuple(#{ik := Value} = Map, Head) ->
552 Len = 16,
553 ?CHECK_LEN(ik, size(Value), Len, Len),
554 encode_auth_tuple(maps:without([ik], Map), <<Head/binary, ?IK, Len, Value:Len/binary>>);
555
556encode_auth_tuple(#{ck := Value} = Map, Head) ->
557 Len = 16,
558 ?CHECK_LEN(ck, size(Value), Len, Len),
559 encode_auth_tuple(maps:without([ck], Map), <<Head/binary, ?CK, Len, Value:Len/binary>>);
560
561encode_auth_tuple(#{autn := Value} = Map, Head) ->
562 Len = 16,
563 ?CHECK_LEN(autn, size(Value), Len, Len),
564 encode_auth_tuple(maps:without([autn], Map), <<Head/binary, ?AUTN, Len, Value:Len/binary>>);
565
566encode_auth_tuple(#{res := Value} = Map, Head) ->
567 Len = size(Value),
568 ?CHECK_LEN(res, size(Value), Len, Len),
569 encode_auth_tuple(maps:without([res], Map), <<Head/binary, ?RES, Len, Value/binary>>);
570
571encode_auth_tuple(#{}, Head) -> Head.
572
573encode_pdp_info(#{pdp_context_id := Value} = Map, Head) ->
574 Len = 1,
575 ?CHECK_SIZE(pdp_context_id, Len, Value),
576 encode_pdp_info(maps:without([pdp_context_id], Map), <<Head/binary, ?PDP_CONTEXT_ID, Len, Value:Len/unit:8>>);
577
578encode_pdp_info(#{pdp_type := Value} = Map, Head) ->
579 Len = 2,
580 ?CHECK_SIZE(pdp_type, Len, Value),
581 encode_pdp_info(maps:without([pdp_type], Map), <<Head/binary, ?PDP_TYPE, Len, Value:Len/unit:8>>);
582
583encode_pdp_info(#{access_point_name := Value} = Map, Head) ->
584 Len = size(Value),
585 ?CHECK_LEN(access_point_name, Len, 1, 100),
586 encode_pdp_info(maps:without([access_point_name], Map), <<Head/binary, ?ACCESS_POINT_NAME, Len, Value/binary>>);
587
588encode_pdp_info(#{quality_of_service := Value} = Map, Head) ->
589 Len = size(Value),
590 ?CHECK_LEN(quality_of_service, Len, 1, 20),
591 encode_pdp_info(maps:without([quality_of_service], Map), <<Head/binary, ?QUALITY_OF_SERVICE, Len, Value/binary>>);
592
593encode_pdp_info(#{pdp_charging := Value} = Map, Head) ->
594 Len = 2,
595 ?CHECK_SIZE(pdp_charging, Len, Value),
596 encode_pdp_info(maps:without([pdp_charging], Map), <<Head/binary, ?PDP_CHARGING, Len, Value:Len/unit:8>>);
597
598encode_pdp_info(#{}, Head) -> Head.
Harald Welteb76c7fa2019-08-22 13:48:00 +0200599
600encode_rat_type(rat_unknown) -> 0;
601encode_rat_type(rat_geran_a) -> 1;
602encode_rat_type(rat_utran_iu) -> 2;
603encode_rat_type(rat_eutran_sgs) -> 3.
604
605encode_rat_types([], Acc) -> list_to_binary(lists:reverse(Acc));
606encode_rat_types([Head|Tail], Acc) ->
607 T = encode_rat_type(Head),
608 encode_rat_types(Tail, [T|Acc]).
609encode_rat_types(List) -> encode_rat_types(List, []).