1%% -*- erlang-indent-level: 2 -*-
2
3-module(bs_decode).
4
5-export([test/0]).
6
7-include("bs_decode_extract.hrl").
8
9-define(PDU, <<30,16,0,90,0,1,0,0,255,255,255,255,81,67,101,7,0,0,0,96,
10	       6,12,146,18,14,0,15,252,16,0,0,17,0,0,128,0,2,241,33,131,
11	       0,20,7,97,112,110,48,49,51,97,8,101,114,105,99,115,115,
12	       111,110,2,115,101,132,0,20,128,192,35,16,1,5,0,16,5,117,
13	       115,101,114,53,5,112,97,115,115,53,133,0,4,172,28,12,1,
14	       133,0,4,172,28,12,3,134,0,8,145,148,113,129,0,0,0,0>>).
15
16-define(RES, {ok,{sesT_createReqV0,
17		  {mvsgT_tid,{mvsgT_imsi,<<81,67,101,7,0,0,0,240>>},6},
18		  [81,67,101,7,0,0,0,96],
19		  {sesT_qualityOfServiceV0,1,4,9,2,18},
20		  0,subscribed,0,0,
21		  {mvsgT_pdpAddressType,ietf_ipv4,[]},
22		  [<<"apn013a">>,<<"ericsson">>,<<"se">>],
23		  {masT_protocolConfigOptions,[],
24		   {masT_pap,true,1,5,"user5","pass5"},
25		   []},
26		  {mvsgT_ipAddress,ipv4,172,28,12,1,0,0,0,0},
27		  {mvsgT_ipAddress,ipv4,172,28,12,3,0,0,0,0},
28		  {mvsT_msisdn,<<145,148,113,129,0,0,0,0>>}},
29	      1}).
30
31test() ->
32    ?RES = decode_v0_opt(42, ?PDU),
33    ok.
34
35decode_v0_opt(0, Pdu) ->
36    decode_gtpc_msg(Pdu);
37decode_v0_opt(N, Pdu) ->
38    decode_gtpc_msg(Pdu),
39    decode_v0_opt(N-1, Pdu).
40
41%%% --------------------------------------------------------------
42%%% #3.1.2 DECODE GTP-C MESSAGE
43%%% --------------------------------------------------------------
44
45%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
46%%% Function   : decode_gtpc_msg(GTP_C_Message)->
47%%%              {ok,Request,ControlDataUs} |
48%%%              {fault,Cause,Request,ControlDataUs}
49%%%
50%%% Types      : GTP_C_Message = binary(), GTP-C message from SGSN
51%%%              Request = record(), Containing decoded request
52%%%              ControlDataUS = record(), Containing header info
53%%%              Cause = integer(), Error code
54%%%
55%%% Description: This function decodes a binary GTP-C message and
56%%%              stores it in a record. Different records are used
57%%%              for different message types.
58%%%
59%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
60
61%%% Create PDP Context Request
62%%% GTP97, SNN=0
63%%% (No SNDCP N-PDU number)
64decode_gtpc_msg(<<0:3,_:4,0:1,16:8,_Length:16,SequenceNumber:16,
65		  _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
66		  TID:8/binary-unit:8,InformationElements/binary>>) ->
67    Errors = #protocolErrors{},
68    {ok,TID2} = tid_internal_storage(TID,[]),
69    EmptyCreateReq = #sesT_createReqV0{tid = TID2,
70				       tidRaw = binary_to_list(TID)},
71    case catch decode_ie_create(InformationElements,0,Errors,EmptyCreateReq) of
72	{ok,CreateReq} ->
73	    {ok,CreateReq,SequenceNumber};
74	{fault,Cause,CreateReq} ->
75	    {fault,Cause,CreateReq,SequenceNumber};
76	{'EXIT',_Reason} ->
77	    {fault,193,EmptyCreateReq,SequenceNumber}
78    end;
79
80%%% Update PDP Context Request
81%%% GTP97, SNN=0
82%%% (No SNDCP N-PDU number)
83decode_gtpc_msg(<<0:3,_:4,0:1,18:8,_Length:16,SequenceNumber:16,
84		  _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
85		  TID:8/binary-unit:8,InformationElements/binary>>) ->
86    io:format("hej", []),
87    Errors = #protocolErrors{},
88    {ok,TID2}=tid_internal_storage(TID,[]),
89    EmptyUpdateReq=#sesT_updateReqV0{tid=TID2,
90				     tidRaw=binary_to_list(TID)},
91    case catch decode_ie_update(InformationElements,0,Errors,
92				EmptyUpdateReq) of
93	{ok,UpdateReq} ->
94	    {ok,UpdateReq,SequenceNumber};
95	{fault,Cause,UpdateReq} ->
96	    {fault,Cause,UpdateReq,SequenceNumber};
97	{'EXIT',Reason} ->
98	    io:format("hej", []),
99	    {fault,193,EmptyUpdateReq,SequenceNumber, Reason}
100    end;
101
102%%% Delete PDP Context Request
103%%% GTP97, SNN=0
104%%% (No SNDCP N-PDU number)
105decode_gtpc_msg(<<0:3,_:4,0:1,20:8,_Length:16,SequenceNumber:16,
106		_FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
107		TID:8/binary-unit:8,_InformationElements/binary>>) ->
108    {ok,TID2} = tid_internal_storage(TID,[]),
109    DeleteReq = #sesT_deleteReqV0{tid=TID2},
110    {ok,DeleteReq,SequenceNumber};
111
112%%% Delete PDP Context Response
113%%% GTP97, SNN=0
114%%% (No SNDCP N-PDU number)
115decode_gtpc_msg(<<0:3,_:4,0:1,21:8,_Length:16,SequenceNumber:16,
116		_FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8,
117		TID:8/binary-unit:8,InformationElements/binary>>) ->
118    {ok,TID2} = tid_internal_storage(TID,[]),
119    EmptyDeleteRes = #sesT_deleteResV0{tid=TID2},
120    case catch decode_ie_delete_res(InformationElements,0,EmptyDeleteRes) of
121	{ok, DeleteRes} ->
122	    {ok,DeleteRes,SequenceNumber};
123	{fault,Cause,DeleteRes} ->
124	    {fault,Cause,DeleteRes,SequenceNumber};
125	{'EXIT',_Reason} ->
126	    {fault,193,EmptyDeleteRes,SequenceNumber}
127    end;
128
129%%% Error handling
130decode_gtpc_msg(_GTP_C_Message) ->
131    {fault}.
132
133%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
134%%% decode_ie_create/4
135%%% Decode information elements for Create PDP Context Request
136
137%%% All elements decoded
138decode_ie_create(<<>>,PresentIEs,Errors,CreateReq) ->
139    %% Check mandatory IE's
140    if
141	(PresentIEs band 16#77D) =/= 16#77D ->
142	    {fault,202,CreateReq}; %Mandatory IE missing
143	true -> %OK
144	    %% Check errors during decoding
145	    case Errors of
146		#protocolErrors{invalidManIE=true} -> %Invalid mandatory IE
147		    {fault,201,CreateReq}; %Mandatory IE incorrect
148		#protocolErrors{outOfSequence=true} -> %Out of sequence
149		    {fault,193,CreateReq}; %Invalid message format
150		#protocolErrors{incorrectOptIE=true} -> %Incorrect optional IE
151		    {fault,203,CreateReq}; %Optional IE incorrect
152		_ -> %OK
153		    {ok,CreateReq}
154	    end
155    end;
156
157%%% Quality of Service Profile, Mandatory
158decode_ie_create(<<6:8,QoSElement:3/binary-unit:8,Rest/binary>>,PresentIEs,
159		 Errors,CreateReq) ->
160    if
161        (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE's, ignore
162            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
163        PresentIEs > 16#00000001 -> %Out of sequence
164            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
165            <<_:2,DelayClass:3,ReliabilityClass:3,
166	      PeakThroughput:4,_:1,PrecedenceClass:3,
167	      _:3,MeanThroughput:5>> = QoSElement,
168            QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
169					 reliabilityClass=ReliabilityClass,
170					 peakThroughput=PeakThroughput,
171					 precedenceClass=PrecedenceClass,
172					 meanThroughput=MeanThroughput},
173            UpdatedCreateReq=CreateReq#sesT_createReqV0{qos=QoS},
174            decode_ie_create(Rest,(PresentIEs bor 16#00000001),
175			     UpdatedErrors,UpdatedCreateReq);
176        true -> %OK
177            <<_:2,DelayClass:3,ReliabilityClass:3,
178	      PeakThroughput:4,_:1,PrecedenceClass:3,
179	      _:3,MeanThroughput:5>> = QoSElement,
180            QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
181					 reliabilityClass=ReliabilityClass,
182					 peakThroughput=PeakThroughput,
183					 precedenceClass=PrecedenceClass,
184					 meanThroughput=MeanThroughput},
185            UpdatedCreateReq=CreateReq#sesT_createReqV0{qos=QoS},
186            decode_ie_create(Rest,(PresentIEs bor 16#00000001),
187			     Errors,UpdatedCreateReq)
188    end;
189
190%%% Recovery, Optional
191decode_ie_create(<<14:8,Recovery:8,Rest/binary>>,
192		 PresentIEs,Errors,CreateReq) ->
193    if
194        (PresentIEs band 16#00000002) =:= 16#00000002 -> %Repeated IE, ignored
195            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
196        PresentIEs > 16#00000002 -> %Out of sequence
197            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
198            UpdatedCreateReq=CreateReq#sesT_createReqV0{recovery=Recovery},
199            decode_ie_create(Rest,(PresentIEs bor 16#00000002),
200                UpdatedErrors,UpdatedCreateReq);
201        true -> %OK
202            UpdatedCreateReq=CreateReq#sesT_createReqV0{recovery=Recovery},
203            decode_ie_create(Rest,(PresentIEs bor 16#00000002),Errors,
204			     UpdatedCreateReq)
205    end;
206
207%%% Selection mode, Mandatory
208decode_ie_create(<<15:8,_:6,SelectionMode:2,Rest/binary>>,PresentIEs,
209		 Errors,CreateReq) ->
210    if
211        (PresentIEs band 16#00000004) =:= 16#00000004 -> %Repeated IE, ignored
212            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
213        PresentIEs > 16#00000004 -> %Out of sequence
214            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
215            UpdatedCreateReq=CreateReq#sesT_createReqV0{
216			       selMode=selection_mode_internal_storage(SelectionMode)},
217            decode_ie_create(Rest,(PresentIEs bor 16#00000004),
218			     UpdatedErrors,UpdatedCreateReq);
219        true -> %OK
220            UpdatedCreateReq=CreateReq#sesT_createReqV0{
221			       selMode=selection_mode_internal_storage(SelectionMode)},
222            decode_ie_create(Rest,(PresentIEs bor 16#00000004),Errors,
223                UpdatedCreateReq)
224    end;
225
226%%% Flow Label Data I, Mandatory
227decode_ie_create(<<16:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,CreateReq) ->
228    if
229        (PresentIEs band 16#00000008) =:= 16#00000008 -> %Repeated IE, ignored
230            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
231        PresentIEs > 16#00000008 -> %Out of sequence
232            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
233            UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblData=FlowLabel},
234            decode_ie_create(Rest,(PresentIEs bor 16#00000008),
235			     UpdatedErrors,UpdatedCreateReq);
236        true -> %OK
237            UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblData=FlowLabel},
238            decode_ie_create(Rest,(PresentIEs bor 16#00000008),Errors,
239			     UpdatedCreateReq)
240    end;
241
242%%% Flow Label Signalling, Mandatory
243decode_ie_create(<<17:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,CreateReq) ->
244    if
245        (PresentIEs band 16#00000010) =:= 16#00000010 -> %Repeated IE, ignored
246            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
247        PresentIEs > 16#00000010 -> %Out of sequence
248            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
249            UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblSig=FlowLabel},
250            decode_ie_create(Rest,(PresentIEs bor 16#00000010),
251			     UpdatedErrors,UpdatedCreateReq);
252        true -> %OK
253            UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblSig=FlowLabel},
254            decode_ie_create(Rest,(PresentIEs bor 16#00000010),Errors,
255			     UpdatedCreateReq)
256    end;
257
258%%% End User Address, Mandatory
259decode_ie_create(<<128:8,Length:16,More/binary>>,PresentIEs,
260		 Errors,CreateReq) ->
261    <<PDPElement:Length/binary-unit:8,Rest/binary>> = More,
262    if
263        (PresentIEs band 16#00000020) =:= 16#00000020 -> %Repeated IE, ignore
264            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
265        PresentIEs > 16#00000020 -> %Out of sequence
266            case pdp_addr_internal_storage(PDPElement) of
267		{ok,PDPAddress} ->
268		    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
269		    UpdatedCreateReq=CreateReq#sesT_createReqV0{endUserAdd=PDPAddress},
270		    decode_ie_create(Rest,(PresentIEs bor 16#00000020),
271				     UpdatedErrors,UpdatedCreateReq);
272		{fault} ->
273		    UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
274							outOfSequence=true},
275		    decode_ie_create(Rest,(PresentIEs bor 16#00000020),
276				     UpdatedErrors,CreateReq)
277	    end;
278        true -> %OK
279	    case pdp_addr_internal_storage(PDPElement) of
280		{ok,PDPAddress} ->
281		    UpdatedCreateReq=CreateReq#sesT_createReqV0{endUserAdd=PDPAddress},
282		    decode_ie_create(Rest,(PresentIEs bor 16#00000020),
283				     Errors,UpdatedCreateReq);
284		{fault} ->
285		    UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
286		    decode_ie_create(Rest,(PresentIEs bor 16#00000020),
287				     UpdatedErrors,CreateReq)
288	    end
289    end;
290
291%%% Access Point Name, Mandatory
292decode_ie_create(<<131:8,Length:16,More/binary>>,PresentIEs,
293		 Errors,CreateReq) ->
294    <<APNElement:Length/binary-unit:8,Rest/binary>> = More,
295    if
296        (PresentIEs band 16#00000040) =:= 16#00000040 -> %Repeated IE, ignore
297            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
298        PresentIEs > 16#00000040 -> %Out of sequence
299            case catch apn_internal_storage(APNElement,[]) of
300		{ok,APN} ->
301		    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
302		    UpdatedCreateReq=CreateReq#sesT_createReqV0{accPointName=APN},
303		    decode_ie_create(Rest,(PresentIEs bor 16#00000040),
304				     UpdatedErrors,UpdatedCreateReq);
305		_ ->
306		    UpdatedErrors=Errors#protocolErrors{outOfSequence=true,
307							invalidManIE=true},
308		    decode_ie_create(Rest,(PresentIEs bor 16#00000040),
309				     UpdatedErrors,CreateReq)
310	    end;
311        true -> %OK
312            case catch apn_internal_storage(APNElement,[]) of
313		{ok,APN} ->
314		    UpdatedCreateReq=CreateReq#sesT_createReqV0{accPointName=APN},
315		    decode_ie_create(Rest,(PresentIEs bor 16#00000040),
316				     Errors,UpdatedCreateReq);
317		_ ->
318		    UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
319		    decode_ie_create(Rest,(PresentIEs bor 16#00000040),
320				     UpdatedErrors,CreateReq)
321	    end
322    end;
323
324%%% Protocol Configuration Options, Optional
325decode_ie_create(<<132:8,Length:16,More/binary>>,PresentIEs,Errors,CreateReq) ->
326    <<ConfigurationElement:Length/binary-unit:8,Rest/binary>> = More,
327    if
328        (PresentIEs band 16#00000080) =:= 16#00000080 -> %Repeated IE, ignore
329            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
330        PresentIEs > 16#00000080 -> %Out of sequence
331            case catch pco_internal_storage(ConfigurationElement) of
332		{ok,PCO} ->
333		    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
334		    UpdatedCreateReq=CreateReq#sesT_createReqV0{protConOpt=PCO},
335		    decode_ie_create(Rest,(PresentIEs bor 16#00000080),
336				     UpdatedErrors,UpdatedCreateReq);
337		_ ->
338		    UpdatedErrors=Errors#protocolErrors{outOfSequence=true,
339							incorrectOptIE=true},
340		    decode_ie_create(Rest,(PresentIEs bor 16#00000080),
341				     UpdatedErrors,CreateReq)
342	    end;
343        true -> %OK
344            case catch pco_internal_storage(ConfigurationElement) of
345		{ok,PCO} ->
346		    UpdatedCreateReq=CreateReq#sesT_createReqV0{protConOpt=PCO},
347		    decode_ie_create(Rest,(PresentIEs bor 16#00000080),
348				     Errors,UpdatedCreateReq);
349		_ ->
350		    UpdatedErrors=Errors#protocolErrors{incorrectOptIE=true},
351		    decode_ie_create(Rest,(PresentIEs bor 16#00000080),
352				     UpdatedErrors,CreateReq)
353	    end
354    end;
355
356%%% SGSN Address for signalling, Mandatory OR SGSN Address for user traffic, Mandatory
357decode_ie_create(<<133:8,Length:16,More/binary>>,PresentIEs,
358		 Errors,CreateReq) ->
359    <<AddressElement:Length/binary-unit:8,Rest/binary>> = More,
360    if
361        (PresentIEs band 16#00000300) =:= 16#00000300 -> %Repeated IE, ignore
362            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
363        PresentIEs > 16#00000200 -> %Out of sequence
364            if
365                (PresentIEs band 16#00000100) =:= 16#00000000 -> %Signalling
366                    case gsn_addr_internal_storage(AddressElement) of
367			{ok,GSNAddr} ->
368			    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
369			    UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddSig=GSNAddr},
370			    decode_ie_create(Rest,(PresentIEs bor 16#00000100),
371					     UpdatedErrors,UpdatedCreateReq);
372			{fault} ->
373			    UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
374								outOfSequence=true},
375			    decode_ie_create(Rest,(PresentIEs bor 16#00000100),
376					     UpdatedErrors,CreateReq)
377		    end;
378                true -> % User traffic
379                    case gsn_addr_internal_storage(AddressElement) of
380			{ok,GSNAddr} ->
381			    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
382			    UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddUser=GSNAddr},
383			    decode_ie_create(Rest,(PresentIEs bor 16#00000200),
384					     UpdatedErrors,UpdatedCreateReq);
385			{fault} ->
386			    UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
387								outOfSequence=true},
388			    decode_ie_create(Rest,(PresentIEs bor 16#00000200),
389					     UpdatedErrors,CreateReq)
390		    end
391            end;
392        PresentIEs < 16#00000100 -> %OK, SGSN Address for signalling
393            case gsn_addr_internal_storage(AddressElement) of
394		{ok,GSNAddr} ->
395		    UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddSig=GSNAddr},
396		    decode_ie_create(Rest,(PresentIEs bor 16#00000100),
397				     Errors,UpdatedCreateReq);
398		{fault} ->
399		    UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
400		    decode_ie_create(Rest,(PresentIEs bor 16#00000100),
401				     UpdatedErrors,CreateReq)
402	    end;
403        true -> %OK, SGSN Address for user traffic
404            case gsn_addr_internal_storage(AddressElement) of
405		{ok,GSNAddr} ->
406		    UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddUser=GSNAddr},
407		    decode_ie_create(Rest,(PresentIEs bor 16#00000200),
408				     Errors,UpdatedCreateReq);
409		{fault} ->
410		    UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
411		    decode_ie_create(Rest,(PresentIEs bor 16#00000200),
412				     UpdatedErrors,CreateReq)
413	    end
414    end;
415
416%%% MSISDN, Mandatory
417decode_ie_create(<<134:8,Length:16,More/binary>>,PresentIEs,
418    Errors,CreateReq) ->
419    <<MSISDNElement:Length/binary-unit:8,Rest/binary>> = More,
420    if
421        (PresentIEs band 16#00000400) =:= 16#00000400 -> %Repeated IE, ignore
422            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
423        PresentIEs > 16#00000400 -> %Out of sequence
424            case msisdn_internal_storage(MSISDNElement,[]) of
425		{ok,MSISDN} ->
426		    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
427		    UpdatedCreateReq=CreateReq#sesT_createReqV0{msisdn=MSISDN},
428		    decode_ie_create(Rest,(PresentIEs bor 16#00000400),
429				     UpdatedErrors,UpdatedCreateReq);
430		{fault} ->
431		    UpdatedErrors=Errors#protocolErrors{outOfSequence=true,invalidManIE=true},
432		    decode_ie_create(Rest,(PresentIEs bor 16#00000400),
433				     UpdatedErrors,CreateReq)
434	    end;
435        true -> %OK
436		    UpdatedCreateReq=CreateReq#sesT_createReqV0{msisdn=#mvsT_msisdn{value=MSISDNElement}},
437		    decode_ie_create(Rest,(PresentIEs bor 16#00000400),
438				     Errors,UpdatedCreateReq)
439
440    end;
441
442%%% Private Extension, Optional
443%%% Not implemented
444
445%%% Error handling, Unexpected or unknown IE
446decode_ie_create(UnexpectedIE,PresentIEs,Errors,CreateReq) ->
447    case check_ie(UnexpectedIE) of
448        {defined_ie,Rest} -> %OK, ignored
449            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
450        {handled_ie,Rest} -> %OK, ignored
451            decode_ie_create(Rest,PresentIEs,Errors,CreateReq);
452        {unhandled_ie} -> %Error, abort decoding
453            {fault,193,CreateReq} %Invalid message format
454    end.
455
456%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
457%%% decode_ie_update/4
458%%% Decode information elements for Update PDP Context Request
459
460%%% All elements decoded
461decode_ie_update(<<>>,PresentIEs,Errors,UpdateReq) ->
462    %% Check mandatory IE's
463    if
464	(PresentIEs band 16#3D) =/= 16#3D ->
465	    {fault,202,UpdateReq}; %Mandatory IE missing
466	true -> %OK
467	    %% Check errors during decoding
468	    case Errors of
469		#protocolErrors{invalidManIE=true} -> %Invalid mandatory IE
470		    {fault,201,UpdateReq}; %Mandatory IE incorrect
471		#protocolErrors{outOfSequence=true} -> %Out of sequence
472		    {fault,193,UpdateReq}; %Invalid message format
473		_ -> %OK
474		    {ok,UpdateReq}
475	    end
476    end;
477
478%%% Quality of Service Profile, Mandatory
479decode_ie_update(<<6:8,QoSElement:3/binary-unit:8,Rest/binary>>,PresentIEs,
480		 Errors,UpdateReq) ->
481    if
482        (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE's, ignore
483            decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
484        PresentIEs > 16#00000001 -> %Out of sequence
485            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
486            <<_:2,DelayClass:3,ReliabilityClass:3,
487	    PeakThroughput:4,_:1,PrecedenceClass:3,
488	    _:3,MeanThroughput:5>> = QoSElement,
489            QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
490					 reliabilityClass=ReliabilityClass,
491					 peakThroughput=PeakThroughput,
492					 precedenceClass=PrecedenceClass,
493					 meanThroughput=MeanThroughput},
494            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{qos=QoS},
495            decode_ie_update(Rest,(PresentIEs bor 16#00000001),
496			     UpdatedErrors,UpdatedUpdateReq);
497        true -> %OK
498            <<_:2,DelayClass:3,ReliabilityClass:3,
499	    PeakThroughput:4,_:1,PrecedenceClass:3,
500	    _:3,MeanThroughput:5>> = QoSElement,
501            QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass,
502					 reliabilityClass=ReliabilityClass,
503					 peakThroughput=PeakThroughput,
504					 precedenceClass=PrecedenceClass,
505					 meanThroughput=MeanThroughput},
506            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{qos=QoS},
507            decode_ie_update(Rest,(PresentIEs bor 16#00000001),
508			     Errors,UpdatedUpdateReq)
509    end;
510
511%%% Recovery, Optional
512decode_ie_update(<<14:8,Recovery:8,Rest/binary>>,PresentIEs,Errors,UpdateReq) ->
513    if
514        (PresentIEs band 16#00000002) =:= 16#00000002 -> %Repeated IE, ignored
515            decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
516        PresentIEs > 16#00000002 -> %Out of sequence
517            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
518            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{recovery=Recovery},
519            decode_ie_update(Rest,(PresentIEs bor 16#00000002),
520                UpdatedErrors,UpdatedUpdateReq);
521        true -> %OK
522            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{recovery=Recovery},
523            decode_ie_update(Rest,(PresentIEs bor 16#00000002),Errors,
524                UpdatedUpdateReq)
525    end;
526
527%%% Flow Label Data I, Mandatory
528decode_ie_update(<<16:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,UpdateReq) ->
529    if
530        (PresentIEs band 16#00000004) =:= 16#00000004 -> %Repeated IE, ignored
531            decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
532        PresentIEs > 16#00000004 -> %Out of sequence
533            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
534            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblData=FlowLabel},
535            decode_ie_update(Rest,(PresentIEs bor 16#00000004),
536                UpdatedErrors,UpdatedUpdateReq);
537        true -> %OK
538            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblData=FlowLabel},
539            decode_ie_update(Rest,(PresentIEs bor 16#00000004),Errors,
540			     UpdatedUpdateReq)
541    end;
542
543%%% Flow Label Signalling, Mandatory
544decode_ie_update(<<17:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,UpdateReq) ->
545    if
546        (PresentIEs band 16#00000008) =:= 16#00000008 -> %Repeated IE, ignored
547            decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
548        PresentIEs > 16#00000008 -> %Out of sequence
549            UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
550            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblSig=FlowLabel},
551            decode_ie_update(Rest,(PresentIEs bor 16#00000008),
552                UpdatedErrors,UpdatedUpdateReq);
553        true -> %OK
554            UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblSig=FlowLabel},
555            decode_ie_update(Rest,(PresentIEs bor 16#00000008),Errors,
556			     UpdatedUpdateReq)
557    end;
558
559%%% SGSN Address for signalling, Mandatory OR SGSN Address for user traffic, Mandatory
560decode_ie_update(<<133:8,Length:16,More/binary>>,PresentIEs,
561    Errors,UpdateReq) ->
562    <<AddressElement:Length/binary-unit:8,Rest/binary>> = More,
563    if
564        (PresentIEs band 16#00000030) =:= 16#00000030 -> %Repeated IE, ignore
565            decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
566        PresentIEs > 16#00000020 -> %Out of sequence
567            if
568                (PresentIEs band 16#00000010) =:= 16#00000000 -> %Signalling
569                    case gsn_addr_internal_storage(AddressElement) of
570			{ok,GSNAddr} ->
571			    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
572			    UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddSig=GSNAddr},
573			    decode_ie_update(Rest,(PresentIEs bor 16#00000010),
574					     UpdatedErrors,UpdatedUpdateReq);
575			{fault} ->
576			    UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
577								outOfSequence=true},
578			    decode_ie_update(Rest,(PresentIEs bor 16#00000010),
579					     UpdatedErrors,UpdateReq)
580		    end;
581                true -> % User traffic
582                    case gsn_addr_internal_storage(AddressElement) of
583			{ok,GSNAddr} ->
584			    UpdatedErrors=Errors#protocolErrors{outOfSequence=true},
585			    UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddUser=GSNAddr},
586			    decode_ie_update(Rest,(PresentIEs bor 16#00000020),
587					     UpdatedErrors,UpdatedUpdateReq);
588			{fault} ->
589			    UpdatedErrors=Errors#protocolErrors{invalidManIE=true,
590								outOfSequence=true},
591			    decode_ie_update(Rest,(PresentIEs bor 16#00000020),
592					     UpdatedErrors,UpdateReq)
593		    end
594            end;
595        PresentIEs < 16#00000010 -> %OK, SGSN Address for signalling
596            case gsn_addr_internal_storage(AddressElement) of
597		{ok,GSNAddr} ->
598		    UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddSig=GSNAddr},
599		    decode_ie_update(Rest,(PresentIEs bor 16#00000010),
600				     Errors,UpdatedUpdateReq);
601		{fault} ->
602		    UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
603		    decode_ie_update(Rest,(PresentIEs bor 16#00000010),
604				     UpdatedErrors,UpdateReq)
605	    end;
606        true -> %OK, SGSN Address for user traffic
607            case gsn_addr_internal_storage(AddressElement) of
608		{ok,GSNAddr} ->
609		    UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddUser=GSNAddr},
610		    decode_ie_update(Rest,(PresentIEs bor 16#00000020),
611				     Errors,UpdatedUpdateReq);
612		{fault} ->
613		    UpdatedErrors=Errors#protocolErrors{invalidManIE=true},
614		    decode_ie_update(Rest,(PresentIEs bor 16#00000020),
615				     UpdatedErrors,UpdateReq)
616	    end
617    end;
618
619%%% Private Extension, Optional
620%%% Not implemented
621
622%%% Error handling, Unexpected or unknown IE
623decode_ie_update(UnexpectedIE,PresentIEs,Errors,UpdateReq) ->
624    case check_ie(UnexpectedIE) of
625        {defined_ie,Rest} -> %OK, ignored
626            decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
627        {handled_ie,Rest} -> %OK, ignored
628            decode_ie_update(Rest,PresentIEs,Errors,UpdateReq);
629        {unhandled_ie} -> %Error, abort decoding
630            {fault,193,UpdateReq} %Invalid message format
631    end.
632
633
634%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
635%%% decode_ie_delete_req/4
636%%% Decode information elements for Delete PDP Context Request
637
638%%% Private Extension, Optional
639%%% Not implemented
640
641
642%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
643%%% decode_ie_delete_res/4
644%%% Decode information elements for Delete PDP Context Response
645
646%%% All elements decoded
647decode_ie_delete_res(<<>>,PresentIEs,DeleteRes) ->
648    %% Check mandatory IE's
649    if
650	(PresentIEs band 16#0001) =/= 16#0001 ->
651	    {fault,202,DeleteRes}; %Mandatory IE missing
652	true -> %OK
653	    {ok,DeleteRes}
654    end;
655
656%%% Cause, Mandatory
657decode_ie_delete_res(<<1:8,Cause:8,Rest/binary>>,PresentIEs,DeleteRes) ->
658    if
659        (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE, ignored
660            decode_ie_delete_res(Rest,PresentIEs,DeleteRes);
661        true -> %OK
662            UpdatedDeleteRes=DeleteRes#sesT_deleteResV0{cause=Cause},
663            decode_ie_delete_res(Rest,(PresentIEs bor 16#00000001),
664		UpdatedDeleteRes)
665     end;
666
667%%% Private Extension, Optional
668%%% Not implemented
669
670%%% Error handling, Unexpected or unknown IE
671decode_ie_delete_res(UnexpectedIE,PresentIEs,DeleteRes) ->
672    case check_ie(UnexpectedIE) of
673        {defined_ie,Rest} -> %OK, ignored
674            decode_ie_delete_res(Rest,PresentIEs,DeleteRes);
675        {handled_ie,Rest} -> %OK, ignored
676            decode_ie_delete_res(Rest,PresentIEs,DeleteRes);
677        {unhandled_ie} -> %Error, abort decoding
678            {fault,193,DeleteRes} %Invalid message format
679    end.
680
681%%% --------------------------------------------------------------
682%%% #3.2 COMMON INTERNAL FUNCTIONS
683%%% --------------------------------------------------------------
684
685%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
686%%% check_ie/1
687%%% Check Information Element, Unexpected or Unknown
688check_ie(<<1:8,_:8,Rest/binary>>) ->
689    {defined_ie,Rest};
690%%% IMSI
691check_ie(<<2:8,_:8/binary-unit:8,Rest/binary>>) ->
692    {defined_ie,Rest};
693%%% RAI
694check_ie(<<3:8,_:6/binary-unit:8,Rest/binary>>) ->
695    {defined_ie,Rest};
696%%% TTLI
697check_ie(<<4:8,_:4/binary-unit:8,Rest/binary>>) ->
698    {defined_ie,Rest};
699%%% P-TMSI
700check_ie(<<5:8,_:4/binary-unit:8,Rest/binary>>) ->
701    {defined_ie,Rest};
702%%% Quality of Service Profile
703check_ie(<<6:8,_:3/binary-unit:8,Rest/binary>>) ->
704    {defined_ie,Rest};
705%%% Reordering Required
706check_ie(<<8:8,_:8,Rest/binary>>) ->
707    {defined_ie,Rest};
708%%% Authentication Triplet
709check_ie(<<9:8,_:28/binary-unit:8,Rest/binary>>) ->
710    {defined_ie,Rest};
711%%% MAP Cause
712check_ie(<<11:8,_:8,Rest/binary>>) ->
713    {defined_ie,Rest};
714%%% P-TMSI Signature
715check_ie(<<12:8,_:3/binary-unit:8,Rest/binary>>) ->
716    {defined_ie,Rest};
717%%% MS Validated
718check_ie(<<13:8,_:8,Rest/binary>>) ->
719    {defined_ie,Rest};
720%%% Recovery
721check_ie(<<14:8,_:8,Rest/binary>>) ->
722    {defined_ie,Rest};
723%%% Selection Mode
724check_ie(<<15:8,_:8,Rest/binary>>) ->
725    {defined_ie,Rest};
726%%% Flow Label Data I
727check_ie(<<16:8,_:16,Rest/binary>>) ->
728    {defined_ie,Rest};
729%%% Flow Label Signalling
730check_ie(<<17:8,_:16,Rest/binary>>) ->
731    {defined_ie,Rest};
732%%% Flow Label Data II
733check_ie(<<18:8,_:32,Rest/binary>>) ->
734    {defined_ie,Rest};
735%%% MS Not Reachable Reason
736check_ie(<<19:8,_:8,Rest/binary>>) ->
737    {defined_ie,Rest};
738%%% Charging ID
739check_ie(<<127:8,_:4/binary-unit:8,Rest/binary>>) ->
740    {defined_ie,Rest};
741%%% TLV element, skipped using Length
742check_ie(<<1:1,_:7,Length:16,More/binary>>) ->
743    if
744        Length > byte_size(More) ->
745            {unhandled_ie};
746        true ->
747            <<_:Length/binary-unit:8,Rest/binary>> = More,
748            {handled_ie,Rest}
749    end;
750%%% TV element, unknown size. Can not be handled.
751check_ie(_UnhandledIE) ->
752    {unhandled_ie}.
753
754%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
755%%% tid_internal_storage/3
756%%% Convert TID binary to internal datatype
757tid_internal_storage(Bin,_) ->
758    Size = byte_size(Bin) - 1,
759    <<Front:Size/binary,NSAPI:4,DigitN:4>> = Bin,
760    Result =
761	case DigitN of
762	    2#1111 ->
763		#mvsgT_tid{imsi = #mvsgT_imsi{value = Front}, nsapi = NSAPI};
764	    _ ->
765		Value = <<Front/binary,2#1111:4,DigitN:4>>,
766		#mvsgT_tid{imsi = #mvsgT_imsi{value = Value}, nsapi = NSAPI}
767	end,
768    {ok,Result}.
769%% tid_internal_storage(<<NSAPI:4,2#1111:4>>,IMSI) ->
770%%      {ok,#mvsgT_tid{imsi=#mvsgT_imsi{value=lists:reverse(IMSI)},
771%%  		  nsapi=NSAPI}};
772%% tid_internal_storage(<<NSAPI:4,DigitN:4>>,IMSI) when
773%%    DigitN < 10 ->
774%%      {ok,#mvsgT_tid{imsi=#mvsgT_imsi{value=lists:reverse([(DigitN bor 2#11110000)|IMSI])},
775%%  		  nsapi=NSAPI}};
776%% tid_internal_storage(<<2#11111111:8,Rest/binary>>,IMSI) ->
777%%      tid_internal_storage(Rest,IMSI);
778%% tid_internal_storage(<<2#1111:4,DigitN:4,Rest/binary>>,IMSI) when
779%%    DigitN < 10 ->
780%%      tid_internal_storage(Rest,[(DigitN bor 2#11110000)|IMSI]);
781%% tid_internal_storage(<<DigitNplus1:4,DigitN:4,Rest/binary>>,IMSI) when
782%%    DigitNplus1 < 10,
783%%    DigitN < 10 ->
784%%      tid_internal_storage(Rest,[((DigitNplus1 bsl 4) bor DigitN)|IMSI]);
785%% tid_internal_storage(_Rest,_IMSI) ->
786%%      {fault}. %% Mandatory IE incorrect
787
788%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
789%%% selection_mode_internal_storage/1
790%%% Convert Selection Mode integer to internal datatype (enum)
791selection_mode_internal_storage(0) ->
792    subscribed;
793selection_mode_internal_storage(1) ->
794    msRequested;
795selection_mode_internal_storage(2) ->
796    sgsnSelected;
797selection_mode_internal_storage(3) ->
798    sgsnSelected.
799
800%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
801%%% pdp_addr_internal_storage/1
802%%% Convert PDP address to internal datatype (record containing
803%%% addresstype and value)
804pdp_addr_internal_storage(<<_:4,0:4,1:8>>) ->
805    {ok,#mvsgT_pdpAddressType{pdpTypeNbr=etsi_ppp,address=[]}};
806pdp_addr_internal_storage(<<_:4,0:4,2:8>>) ->
807    {ok,#mvsgT_pdpAddressType{pdpTypeNbr=etsi_osp_ihoss,address=[]}};
808pdp_addr_internal_storage(<<_:4,1:4,16#21:8>>) ->
809    {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv4,address=[]}};
810pdp_addr_internal_storage(<<_:4,1:4,16#21:8,IP_A:8,IP_B:8,IP_C:8,IP_D:8>>) ->
811    {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv4,
812        address=[IP_A,IP_B,IP_C,IP_D]}};
813pdp_addr_internal_storage(<<_:4,1:4,16#57:8,IP_A:16,IP_B:16,IP_C:16,IP_D:16,
814    IP_E:16,IP_F:16,IP_G:16,IP_H:16>>) ->
815    {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv6,
816        address=[IP_A,IP_B,IP_C,IP_D,IP_E,IP_F,IP_G,IP_H]}};
817pdp_addr_internal_storage(_PDP_ADDR) ->
818    {fault}.
819
820%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
821%%% apn_internal_storage/2
822%%% Convert APN to internal datatype (List containing APN labels)
823apn_internal_storage(<<>>,APN) ->
824    {ok,lists:reverse(APN)};
825apn_internal_storage(<<Length:8,Rest/binary>>,APN) ->
826    <<Label:Length/binary-unit:8,MoreAPNLabels/binary>> = Rest,
827    apn_internal_storage(MoreAPNLabels,[Label|APN]).
828
829%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
830%%% pco_internal_storage/1
831%%% Convert Protocol Configuration Options to internal datatype.
832%%% Implemented configuration options:
833%%% For PPP:
834%%% LCP - Not implemented
835%%% PAP - Authenticate request
836%%% CHAP - Challenge
837%%%      - Response
838%%% IPCP - IP-Address
839%%% For OSP:IHOSS
840%%% Nothing implemented
841pco_internal_storage(<<1:1,_:4,0:3,PPPConfigurationOptions/binary>>) ->
842    case ppp_configuration_options(PPPConfigurationOptions,
843				   #masT_pap{exists=false},[],[]) of
844	{ok,PAP,CHAP,IPCP} ->
845	    {ok,#masT_protocolConfigOptions{pap=PAP,chap=CHAP,ipcp=IPCP}};
846	{fault} ->
847	    {fault}
848    end;
849pco_internal_storage(<<1:1,_:4,1:3,_OSP_IHOSSConfigurationOptions/binary>>) ->
850    {ok,osp_ihoss};
851pco_internal_storage(_UnknownConfigurationOptions) ->
852    {fault}. %% Optional IE incorrect
853
854ppp_configuration_options(<<>>,PAP,CHAP,IPCP) ->
855    {ok,PAP,CHAP,IPCP};
856ppp_configuration_options(<<16#C021:16,Length:8,More/binary>>,PAP,CHAP,IPCP) ->
857    %% LCP - Not implemented
858    <<_LCP:Length/binary-unit:8,Rest/binary>> = More,
859    ppp_configuration_options(Rest,PAP,CHAP,IPCP);
860ppp_configuration_options(<<16#C023:16,_Length:8,1:8,Identifier:8,DataLength:16,
861			  More/binary>>,_PAP,CHAP,IPCP) ->
862    %% PAP - Authenticate request
863    ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself
864    <<Data:ActualDataLength/binary-unit:8,Rest/binary>> = More,
865    <<PeerIDLength:8,PeerData/binary>> = Data,
866    <<PeerID:PeerIDLength/binary-unit:8,PasswdLength:8,PasswordData/binary>> = PeerData,
867    <<Password:PasswdLength/binary,_Padding/binary>> = PasswordData,
868    ppp_configuration_options(Rest,#masT_pap{exists=true,code=1,id=Identifier,
869					     username=binary_to_list(PeerID),
870					     password=binary_to_list(Password)},CHAP,IPCP);
871
872ppp_configuration_options(<<16#C023:16,Length:8,More/binary>>,PAP,CHAP,IPCP) ->
873    %% PAP - Other, not implemented
874    <<_PAP:Length/binary-unit:8,Rest/binary>> = More,
875    ppp_configuration_options(Rest,PAP,CHAP,IPCP);
876ppp_configuration_options(<<16#C223:16,_Length:8,1:8,Identifier:8,DataLength:16,
877			 More/binary>>,PAP,CHAP,IPCP) ->
878    %% CHAP - Challenge
879    ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself
880    <<Data:ActualDataLength/binary-unit:8,Rest/binary>> = More,
881    <<ValueSize:8,ValueAndName/binary>> = Data,
882    <<Value:ValueSize/binary-unit:8,Name/binary>> = ValueAndName,
883    ppp_configuration_options(Rest,PAP,[#masT_chap{code=1,id=Identifier,
884						   value=binary_to_list(Value),
885						   name=binary_to_list(Name)}|CHAP],
886			      IPCP);
887ppp_configuration_options(<<16#C223:16,_Length:8,2:8,Identifier:8,DataLength:16,
888			 More/binary>>,PAP,CHAP,IPCP) ->
889    %% CHAP - Response
890    ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself
891    <<Data:ActualDataLength/binary-unit:8,Rest/binary>> = More,
892    <<ValueSize:8,ValueAndName/binary>> = Data,
893    <<Value:ValueSize/binary-unit:8,Name/binary>> = ValueAndName,
894    ppp_configuration_options(Rest,PAP,[#masT_chap{code=2,id=Identifier,
895						   value=binary_to_list(Value),
896						   name=binary_to_list(Name)}|CHAP],
897			      IPCP);
898ppp_configuration_options(<<16#C223:16,Length:8,More/binary>>,PAP,CHAP,IPCP) ->
899    %% CHAP - Other, not implemented
900    <<_CHAP:Length/binary-unit:8,Rest/binary>> = More,
901    ppp_configuration_options(Rest,PAP,CHAP,IPCP);
902ppp_configuration_options(<<16#8021:16,_Length:8,1:8,Identifier:8,OptionsLength:16,
903			  More/binary>>,PAP,CHAP,IPCP) ->
904    %% IPCP - Configure request
905    ActualOptionsLength=OptionsLength-4, %% OptionsLength includes Code, Identifier and itself
906    <<Options:ActualOptionsLength/binary-unit:8,Rest/binary>> = More,
907    case Options of
908	<<3:8,6:8,A1:8,A2:8,A3:8,A4:8>> ->
909	    %% IP Address, version 4
910	    ppp_configuration_options(Rest,PAP,CHAP,
911				      [#masT_ipcp{exists=true,code=1,
912						 id=Identifier,
913						 ipcpList=[#masT_ipcpData{type=3,ipAddress=
914									  #mvsgT_ipAddress{version=ipv4,
915											   a1=A1,a2=A2,
916											   a3=A3,a4=A4,
917											   a5=0,a6=0,
918											   a7=0,a8=0},
919									  rawMessage=binary_to_list(Options)}]}|IPCP]);
920	<<129:8,6:8,B1:8,B2:8,B3:8,B4:8>> ->
921	     %% IP Address, version 4
922	     ppp_configuration_options(Rest,PAP,CHAP,
923				      [#masT_ipcp{exists=true,code=1,
924						 id=Identifier,
925						 ipcpList=[#masT_ipcpData{type=129,ipAddress=
926									  #mvsgT_ipAddress{version=ipv4,
927											   a1=B1,a2=B2,
928											   a3=B3,a4=B4},
929									  rawMessage=binary_to_list(Options)}]}|IPCP]);
930
931	<<131:8,6:8,C1:8,C2:8,C3:8,C4:8>> ->
932	    %% IP Address, version 4
933	    ppp_configuration_options(Rest,PAP,CHAP,
934				      [#masT_ipcp{exists=true,code=1,
935						 id=Identifier,
936						 ipcpList=[#masT_ipcpData{type=131,ipAddress=
937									  #mvsgT_ipAddress{version=ipv4,
938											   a1=C1,a2=C2,
939											   a3=C3,a4=C4},
940									  rawMessage=binary_to_list(Options)}]}|IPCP]);
941	_ ->
942	    ppp_configuration_options(Rest,PAP,CHAP,IPCP)
943    end;
944ppp_configuration_options(<<_UnknownProtocolID:16,Length:8,More/binary>>,
945			  PAP,CHAP,IPCP) ->
946    <<_Skipped:Length/binary-unit:8,Rest/binary>> = More,
947    ppp_configuration_options(Rest,PAP,CHAP,IPCP);
948ppp_configuration_options(_Unhandled,_PAP,_CHAP,_IPCP) ->
949    {fault}.
950
951%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
952%%% gsn_addr_internal_storage/1
953%%% Convert GSN Address to internal datatype
954gsn_addr_internal_storage(<<IP_A:8,IP_B:8,IP_C:8,IP_D:8>>) ->
955    {ok,#mvsgT_ipAddress{version=ipv4,a1=IP_A,a2=IP_B,a3=IP_C,a4=IP_D,a5=0,a6=0,a7=0,a8=0}};
956gsn_addr_internal_storage(<<IP_A:16,IP_B:16,IP_C:16,IP_D:16,
957    IP_E:16,IP_F:16,IP_G:16,IP_H:16>>) ->
958    {ok,#mvsgT_ipAddress{version=ipv6,a1=IP_A,a2=IP_B,a3=IP_C,a4=IP_D,
959        a5=IP_E,a6=IP_F,a7=IP_G,a8=IP_H}};
960gsn_addr_internal_storage(_GSN_ADDR) ->
961    {fault}.
962
963%%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
964%%% msisdn_internal_storage/3
965%%% Convert MSISDN binary to internal datatype (TBCD-octet list)
966
967msisdn_internal_storage(<<>>,MSISDN) ->
968    {ok,#mvsT_msisdn{value=lists:reverse(MSISDN)}};
969msisdn_internal_storage(<<2#11111111:8,_Rest/binary>>,MSISDN) ->
970    {ok,#mvsT_msisdn{value=lists:reverse(MSISDN)}};
971msisdn_internal_storage(<<2#1111:4,DigitN:4,_Rest/binary>>,MSISDN) when
972      DigitN < 10 ->
973    {ok,#mvsT_msisdn{value=lists:reverse([(DigitN bor 2#11110000)|MSISDN])}};
974msisdn_internal_storage(<<DigitNplus1:4,DigitN:4,Rest/binary>>,MSISDN) when
975      DigitNplus1 < 10,
976      DigitN < 10 ->
977    NewMSISDN=[((DigitNplus1 bsl 4) bor DigitN)|MSISDN],
978    msisdn_internal_storage(Rest,NewMSISDN);
979msisdn_internal_storage(_Rest,_MSISDN) ->
980    {fault}. %% Mandatory IE incorrect
981