%% -*- erlang-indent-level: 2 -*- -module(bs_decode). -export([test/0]). -include("bs_decode_extract.hrl"). -define(PDU, <<30,16,0,90,0,1,0,0,255,255,255,255,81,67,101,7,0,0,0,96, 6,12,146,18,14,0,15,252,16,0,0,17,0,0,128,0,2,241,33,131, 0,20,7,97,112,110,48,49,51,97,8,101,114,105,99,115,115, 111,110,2,115,101,132,0,20,128,192,35,16,1,5,0,16,5,117, 115,101,114,53,5,112,97,115,115,53,133,0,4,172,28,12,1, 133,0,4,172,28,12,3,134,0,8,145,148,113,129,0,0,0,0>>). -define(RES, {ok,{sesT_createReqV0, {mvsgT_tid,{mvsgT_imsi,<<81,67,101,7,0,0,0,240>>},6}, [81,67,101,7,0,0,0,96], {sesT_qualityOfServiceV0,1,4,9,2,18}, 0,subscribed,0,0, {mvsgT_pdpAddressType,ietf_ipv4,[]}, [<<"apn013a">>,<<"ericsson">>,<<"se">>], {masT_protocolConfigOptions,[], {masT_pap,true,1,5,"user5","pass5"}, []}, {mvsgT_ipAddress,ipv4,172,28,12,1,0,0,0,0}, {mvsgT_ipAddress,ipv4,172,28,12,3,0,0,0,0}, {mvsT_msisdn,<<145,148,113,129,0,0,0,0>>}}, 1}). test() -> ?RES = decode_v0_opt(42, ?PDU), ok. decode_v0_opt(0, Pdu) -> decode_gtpc_msg(Pdu); decode_v0_opt(N, Pdu) -> decode_gtpc_msg(Pdu), decode_v0_opt(N-1, Pdu). %%% -------------------------------------------------------------- %%% #3.1.2 DECODE GTP-C MESSAGE %%% -------------------------------------------------------------- %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% Function : decode_gtpc_msg(GTP_C_Message)-> %%% {ok,Request,ControlDataUs} | %%% {fault,Cause,Request,ControlDataUs} %%% %%% Types : GTP_C_Message = binary(), GTP-C message from SGSN %%% Request = record(), Containing decoded request %%% ControlDataUS = record(), Containing header info %%% Cause = integer(), Error code %%% %%% Description: This function decodes a binary GTP-C message and %%% stores it in a record. Different records are used %%% for different message types. %%% %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% Create PDP Context Request %%% GTP97, SNN=0 %%% (No SNDCP N-PDU number) decode_gtpc_msg(<<0:3,_:4,0:1,16:8,_Length:16,SequenceNumber:16, _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8, TID:8/binary-unit:8,InformationElements/binary>>) -> Errors = #protocolErrors{}, {ok,TID2} = tid_internal_storage(TID,[]), EmptyCreateReq = #sesT_createReqV0{tid = TID2, tidRaw = binary_to_list(TID)}, case catch decode_ie_create(InformationElements,0,Errors,EmptyCreateReq) of {ok,CreateReq} -> {ok,CreateReq,SequenceNumber}; {fault,Cause,CreateReq} -> {fault,Cause,CreateReq,SequenceNumber}; {'EXIT',_Reason} -> {fault,193,EmptyCreateReq,SequenceNumber} end; %%% Update PDP Context Request %%% GTP97, SNN=0 %%% (No SNDCP N-PDU number) decode_gtpc_msg(<<0:3,_:4,0:1,18:8,_Length:16,SequenceNumber:16, _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8, TID:8/binary-unit:8,InformationElements/binary>>) -> io:format("hej", []), Errors = #protocolErrors{}, {ok,TID2}=tid_internal_storage(TID,[]), EmptyUpdateReq=#sesT_updateReqV0{tid=TID2, tidRaw=binary_to_list(TID)}, case catch decode_ie_update(InformationElements,0,Errors, EmptyUpdateReq) of {ok,UpdateReq} -> {ok,UpdateReq,SequenceNumber}; {fault,Cause,UpdateReq} -> {fault,Cause,UpdateReq,SequenceNumber}; {'EXIT',Reason} -> io:format("hej", []), {fault,193,EmptyUpdateReq,SequenceNumber, Reason} end; %%% Delete PDP Context Request %%% GTP97, SNN=0 %%% (No SNDCP N-PDU number) decode_gtpc_msg(<<0:3,_:4,0:1,20:8,_Length:16,SequenceNumber:16, _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8, TID:8/binary-unit:8,_InformationElements/binary>>) -> {ok,TID2} = tid_internal_storage(TID,[]), DeleteReq = #sesT_deleteReqV0{tid=TID2}, {ok,DeleteReq,SequenceNumber}; %%% Delete PDP Context Response %%% GTP97, SNN=0 %%% (No SNDCP N-PDU number) decode_gtpc_msg(<<0:3,_:4,0:1,21:8,_Length:16,SequenceNumber:16, _FlowLabel:16,_SNDCP_N_PDU_Number:8,_:3/binary-unit:8, TID:8/binary-unit:8,InformationElements/binary>>) -> {ok,TID2} = tid_internal_storage(TID,[]), EmptyDeleteRes = #sesT_deleteResV0{tid=TID2}, case catch decode_ie_delete_res(InformationElements,0,EmptyDeleteRes) of {ok, DeleteRes} -> {ok,DeleteRes,SequenceNumber}; {fault,Cause,DeleteRes} -> {fault,Cause,DeleteRes,SequenceNumber}; {'EXIT',_Reason} -> {fault,193,EmptyDeleteRes,SequenceNumber} end; %%% Error handling decode_gtpc_msg(_GTP_C_Message) -> {fault}. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% decode_ie_create/4 %%% Decode information elements for Create PDP Context Request %%% All elements decoded decode_ie_create(<<>>,PresentIEs,Errors,CreateReq) -> %% Check mandatory IE's if (PresentIEs band 16#77D) =/= 16#77D -> {fault,202,CreateReq}; %Mandatory IE missing true -> %OK %% Check errors during decoding case Errors of #protocolErrors{invalidManIE=true} -> %Invalid mandatory IE {fault,201,CreateReq}; %Mandatory IE incorrect #protocolErrors{outOfSequence=true} -> %Out of sequence {fault,193,CreateReq}; %Invalid message format #protocolErrors{incorrectOptIE=true} -> %Incorrect optional IE {fault,203,CreateReq}; %Optional IE incorrect _ -> %OK {ok,CreateReq} end end; %%% Quality of Service Profile, Mandatory decode_ie_create(<<6:8,QoSElement:3/binary-unit:8,Rest/binary>>,PresentIEs, Errors,CreateReq) -> if (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE's, ignore decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000001 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, <<_:2,DelayClass:3,ReliabilityClass:3, PeakThroughput:4,_:1,PrecedenceClass:3, _:3,MeanThroughput:5>> = QoSElement, QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass, reliabilityClass=ReliabilityClass, peakThroughput=PeakThroughput, precedenceClass=PrecedenceClass, meanThroughput=MeanThroughput}, UpdatedCreateReq=CreateReq#sesT_createReqV0{qos=QoS}, decode_ie_create(Rest,(PresentIEs bor 16#00000001), UpdatedErrors,UpdatedCreateReq); true -> %OK <<_:2,DelayClass:3,ReliabilityClass:3, PeakThroughput:4,_:1,PrecedenceClass:3, _:3,MeanThroughput:5>> = QoSElement, QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass, reliabilityClass=ReliabilityClass, peakThroughput=PeakThroughput, precedenceClass=PrecedenceClass, meanThroughput=MeanThroughput}, UpdatedCreateReq=CreateReq#sesT_createReqV0{qos=QoS}, decode_ie_create(Rest,(PresentIEs bor 16#00000001), Errors,UpdatedCreateReq) end; %%% Recovery, Optional decode_ie_create(<<14:8,Recovery:8,Rest/binary>>, PresentIEs,Errors,CreateReq) -> if (PresentIEs band 16#00000002) =:= 16#00000002 -> %Repeated IE, ignored decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000002 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{recovery=Recovery}, decode_ie_create(Rest,(PresentIEs bor 16#00000002), UpdatedErrors,UpdatedCreateReq); true -> %OK UpdatedCreateReq=CreateReq#sesT_createReqV0{recovery=Recovery}, decode_ie_create(Rest,(PresentIEs bor 16#00000002),Errors, UpdatedCreateReq) end; %%% Selection mode, Mandatory decode_ie_create(<<15:8,_:6,SelectionMode:2,Rest/binary>>,PresentIEs, Errors,CreateReq) -> if (PresentIEs band 16#00000004) =:= 16#00000004 -> %Repeated IE, ignored decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000004 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{ selMode=selection_mode_internal_storage(SelectionMode)}, decode_ie_create(Rest,(PresentIEs bor 16#00000004), UpdatedErrors,UpdatedCreateReq); true -> %OK UpdatedCreateReq=CreateReq#sesT_createReqV0{ selMode=selection_mode_internal_storage(SelectionMode)}, decode_ie_create(Rest,(PresentIEs bor 16#00000004),Errors, UpdatedCreateReq) end; %%% Flow Label Data I, Mandatory decode_ie_create(<<16:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,CreateReq) -> if (PresentIEs band 16#00000008) =:= 16#00000008 -> %Repeated IE, ignored decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000008 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblData=FlowLabel}, decode_ie_create(Rest,(PresentIEs bor 16#00000008), UpdatedErrors,UpdatedCreateReq); true -> %OK UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblData=FlowLabel}, decode_ie_create(Rest,(PresentIEs bor 16#00000008),Errors, UpdatedCreateReq) end; %%% Flow Label Signalling, Mandatory decode_ie_create(<<17:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,CreateReq) -> if (PresentIEs band 16#00000010) =:= 16#00000010 -> %Repeated IE, ignored decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000010 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblSig=FlowLabel}, decode_ie_create(Rest,(PresentIEs bor 16#00000010), UpdatedErrors,UpdatedCreateReq); true -> %OK UpdatedCreateReq=CreateReq#sesT_createReqV0{flowLblSig=FlowLabel}, decode_ie_create(Rest,(PresentIEs bor 16#00000010),Errors, UpdatedCreateReq) end; %%% End User Address, Mandatory decode_ie_create(<<128:8,Length:16,More/binary>>,PresentIEs, Errors,CreateReq) -> <> = More, if (PresentIEs band 16#00000020) =:= 16#00000020 -> %Repeated IE, ignore decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000020 -> %Out of sequence case pdp_addr_internal_storage(PDPElement) of {ok,PDPAddress} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{endUserAdd=PDPAddress}, decode_ie_create(Rest,(PresentIEs bor 16#00000020), UpdatedErrors,UpdatedCreateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true, outOfSequence=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000020), UpdatedErrors,CreateReq) end; true -> %OK case pdp_addr_internal_storage(PDPElement) of {ok,PDPAddress} -> UpdatedCreateReq=CreateReq#sesT_createReqV0{endUserAdd=PDPAddress}, decode_ie_create(Rest,(PresentIEs bor 16#00000020), Errors,UpdatedCreateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000020), UpdatedErrors,CreateReq) end end; %%% Access Point Name, Mandatory decode_ie_create(<<131:8,Length:16,More/binary>>,PresentIEs, Errors,CreateReq) -> <> = More, if (PresentIEs band 16#00000040) =:= 16#00000040 -> %Repeated IE, ignore decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000040 -> %Out of sequence case catch apn_internal_storage(APNElement,[]) of {ok,APN} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{accPointName=APN}, decode_ie_create(Rest,(PresentIEs bor 16#00000040), UpdatedErrors,UpdatedCreateReq); _ -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true, invalidManIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000040), UpdatedErrors,CreateReq) end; true -> %OK case catch apn_internal_storage(APNElement,[]) of {ok,APN} -> UpdatedCreateReq=CreateReq#sesT_createReqV0{accPointName=APN}, decode_ie_create(Rest,(PresentIEs bor 16#00000040), Errors,UpdatedCreateReq); _ -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000040), UpdatedErrors,CreateReq) end end; %%% Protocol Configuration Options, Optional decode_ie_create(<<132:8,Length:16,More/binary>>,PresentIEs,Errors,CreateReq) -> <> = More, if (PresentIEs band 16#00000080) =:= 16#00000080 -> %Repeated IE, ignore decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000080 -> %Out of sequence case catch pco_internal_storage(ConfigurationElement) of {ok,PCO} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{protConOpt=PCO}, decode_ie_create(Rest,(PresentIEs bor 16#00000080), UpdatedErrors,UpdatedCreateReq); _ -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true, incorrectOptIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000080), UpdatedErrors,CreateReq) end; true -> %OK case catch pco_internal_storage(ConfigurationElement) of {ok,PCO} -> UpdatedCreateReq=CreateReq#sesT_createReqV0{protConOpt=PCO}, decode_ie_create(Rest,(PresentIEs bor 16#00000080), Errors,UpdatedCreateReq); _ -> UpdatedErrors=Errors#protocolErrors{incorrectOptIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000080), UpdatedErrors,CreateReq) end end; %%% SGSN Address for signalling, Mandatory OR SGSN Address for user traffic, Mandatory decode_ie_create(<<133:8,Length:16,More/binary>>,PresentIEs, Errors,CreateReq) -> <> = More, if (PresentIEs band 16#00000300) =:= 16#00000300 -> %Repeated IE, ignore decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000200 -> %Out of sequence if (PresentIEs band 16#00000100) =:= 16#00000000 -> %Signalling case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddSig=GSNAddr}, decode_ie_create(Rest,(PresentIEs bor 16#00000100), UpdatedErrors,UpdatedCreateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true, outOfSequence=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000100), UpdatedErrors,CreateReq) end; true -> % User traffic case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddUser=GSNAddr}, decode_ie_create(Rest,(PresentIEs bor 16#00000200), UpdatedErrors,UpdatedCreateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true, outOfSequence=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000200), UpdatedErrors,CreateReq) end end; PresentIEs < 16#00000100 -> %OK, SGSN Address for signalling case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddSig=GSNAddr}, decode_ie_create(Rest,(PresentIEs bor 16#00000100), Errors,UpdatedCreateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000100), UpdatedErrors,CreateReq) end; true -> %OK, SGSN Address for user traffic case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedCreateReq=CreateReq#sesT_createReqV0{sgsnAddUser=GSNAddr}, decode_ie_create(Rest,(PresentIEs bor 16#00000200), Errors,UpdatedCreateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000200), UpdatedErrors,CreateReq) end end; %%% MSISDN, Mandatory decode_ie_create(<<134:8,Length:16,More/binary>>,PresentIEs, Errors,CreateReq) -> <> = More, if (PresentIEs band 16#00000400) =:= 16#00000400 -> %Repeated IE, ignore decode_ie_create(Rest,PresentIEs,Errors,CreateReq); PresentIEs > 16#00000400 -> %Out of sequence case msisdn_internal_storage(MSISDNElement,[]) of {ok,MSISDN} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedCreateReq=CreateReq#sesT_createReqV0{msisdn=MSISDN}, decode_ie_create(Rest,(PresentIEs bor 16#00000400), UpdatedErrors,UpdatedCreateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true,invalidManIE=true}, decode_ie_create(Rest,(PresentIEs bor 16#00000400), UpdatedErrors,CreateReq) end; true -> %OK UpdatedCreateReq=CreateReq#sesT_createReqV0{msisdn=#mvsT_msisdn{value=MSISDNElement}}, decode_ie_create(Rest,(PresentIEs bor 16#00000400), Errors,UpdatedCreateReq) end; %%% Private Extension, Optional %%% Not implemented %%% Error handling, Unexpected or unknown IE decode_ie_create(UnexpectedIE,PresentIEs,Errors,CreateReq) -> case check_ie(UnexpectedIE) of {defined_ie,Rest} -> %OK, ignored decode_ie_create(Rest,PresentIEs,Errors,CreateReq); {handled_ie,Rest} -> %OK, ignored decode_ie_create(Rest,PresentIEs,Errors,CreateReq); {unhandled_ie} -> %Error, abort decoding {fault,193,CreateReq} %Invalid message format end. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% decode_ie_update/4 %%% Decode information elements for Update PDP Context Request %%% All elements decoded decode_ie_update(<<>>,PresentIEs,Errors,UpdateReq) -> %% Check mandatory IE's if (PresentIEs band 16#3D) =/= 16#3D -> {fault,202,UpdateReq}; %Mandatory IE missing true -> %OK %% Check errors during decoding case Errors of #protocolErrors{invalidManIE=true} -> %Invalid mandatory IE {fault,201,UpdateReq}; %Mandatory IE incorrect #protocolErrors{outOfSequence=true} -> %Out of sequence {fault,193,UpdateReq}; %Invalid message format _ -> %OK {ok,UpdateReq} end end; %%% Quality of Service Profile, Mandatory decode_ie_update(<<6:8,QoSElement:3/binary-unit:8,Rest/binary>>,PresentIEs, Errors,UpdateReq) -> if (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE's, ignore decode_ie_update(Rest,PresentIEs,Errors,UpdateReq); PresentIEs > 16#00000001 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, <<_:2,DelayClass:3,ReliabilityClass:3, PeakThroughput:4,_:1,PrecedenceClass:3, _:3,MeanThroughput:5>> = QoSElement, QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass, reliabilityClass=ReliabilityClass, peakThroughput=PeakThroughput, precedenceClass=PrecedenceClass, meanThroughput=MeanThroughput}, UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{qos=QoS}, decode_ie_update(Rest,(PresentIEs bor 16#00000001), UpdatedErrors,UpdatedUpdateReq); true -> %OK <<_:2,DelayClass:3,ReliabilityClass:3, PeakThroughput:4,_:1,PrecedenceClass:3, _:3,MeanThroughput:5>> = QoSElement, QoS=#sesT_qualityOfServiceV0{delayClass=DelayClass, reliabilityClass=ReliabilityClass, peakThroughput=PeakThroughput, precedenceClass=PrecedenceClass, meanThroughput=MeanThroughput}, UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{qos=QoS}, decode_ie_update(Rest,(PresentIEs bor 16#00000001), Errors,UpdatedUpdateReq) end; %%% Recovery, Optional decode_ie_update(<<14:8,Recovery:8,Rest/binary>>,PresentIEs,Errors,UpdateReq) -> if (PresentIEs band 16#00000002) =:= 16#00000002 -> %Repeated IE, ignored decode_ie_update(Rest,PresentIEs,Errors,UpdateReq); PresentIEs > 16#00000002 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{recovery=Recovery}, decode_ie_update(Rest,(PresentIEs bor 16#00000002), UpdatedErrors,UpdatedUpdateReq); true -> %OK UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{recovery=Recovery}, decode_ie_update(Rest,(PresentIEs bor 16#00000002),Errors, UpdatedUpdateReq) end; %%% Flow Label Data I, Mandatory decode_ie_update(<<16:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,UpdateReq) -> if (PresentIEs band 16#00000004) =:= 16#00000004 -> %Repeated IE, ignored decode_ie_update(Rest,PresentIEs,Errors,UpdateReq); PresentIEs > 16#00000004 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblData=FlowLabel}, decode_ie_update(Rest,(PresentIEs bor 16#00000004), UpdatedErrors,UpdatedUpdateReq); true -> %OK UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblData=FlowLabel}, decode_ie_update(Rest,(PresentIEs bor 16#00000004),Errors, UpdatedUpdateReq) end; %%% Flow Label Signalling, Mandatory decode_ie_update(<<17:8,FlowLabel:16,Rest/binary>>,PresentIEs,Errors,UpdateReq) -> if (PresentIEs band 16#00000008) =:= 16#00000008 -> %Repeated IE, ignored decode_ie_update(Rest,PresentIEs,Errors,UpdateReq); PresentIEs > 16#00000008 -> %Out of sequence UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblSig=FlowLabel}, decode_ie_update(Rest,(PresentIEs bor 16#00000008), UpdatedErrors,UpdatedUpdateReq); true -> %OK UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{flowLblSig=FlowLabel}, decode_ie_update(Rest,(PresentIEs bor 16#00000008),Errors, UpdatedUpdateReq) end; %%% SGSN Address for signalling, Mandatory OR SGSN Address for user traffic, Mandatory decode_ie_update(<<133:8,Length:16,More/binary>>,PresentIEs, Errors,UpdateReq) -> <> = More, if (PresentIEs band 16#00000030) =:= 16#00000030 -> %Repeated IE, ignore decode_ie_update(Rest,PresentIEs,Errors,UpdateReq); PresentIEs > 16#00000020 -> %Out of sequence if (PresentIEs band 16#00000010) =:= 16#00000000 -> %Signalling case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddSig=GSNAddr}, decode_ie_update(Rest,(PresentIEs bor 16#00000010), UpdatedErrors,UpdatedUpdateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true, outOfSequence=true}, decode_ie_update(Rest,(PresentIEs bor 16#00000010), UpdatedErrors,UpdateReq) end; true -> % User traffic case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedErrors=Errors#protocolErrors{outOfSequence=true}, UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddUser=GSNAddr}, decode_ie_update(Rest,(PresentIEs bor 16#00000020), UpdatedErrors,UpdatedUpdateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true, outOfSequence=true}, decode_ie_update(Rest,(PresentIEs bor 16#00000020), UpdatedErrors,UpdateReq) end end; PresentIEs < 16#00000010 -> %OK, SGSN Address for signalling case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddSig=GSNAddr}, decode_ie_update(Rest,(PresentIEs bor 16#00000010), Errors,UpdatedUpdateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true}, decode_ie_update(Rest,(PresentIEs bor 16#00000010), UpdatedErrors,UpdateReq) end; true -> %OK, SGSN Address for user traffic case gsn_addr_internal_storage(AddressElement) of {ok,GSNAddr} -> UpdatedUpdateReq=UpdateReq#sesT_updateReqV0{sgsnAddUser=GSNAddr}, decode_ie_update(Rest,(PresentIEs bor 16#00000020), Errors,UpdatedUpdateReq); {fault} -> UpdatedErrors=Errors#protocolErrors{invalidManIE=true}, decode_ie_update(Rest,(PresentIEs bor 16#00000020), UpdatedErrors,UpdateReq) end end; %%% Private Extension, Optional %%% Not implemented %%% Error handling, Unexpected or unknown IE decode_ie_update(UnexpectedIE,PresentIEs,Errors,UpdateReq) -> case check_ie(UnexpectedIE) of {defined_ie,Rest} -> %OK, ignored decode_ie_update(Rest,PresentIEs,Errors,UpdateReq); {handled_ie,Rest} -> %OK, ignored decode_ie_update(Rest,PresentIEs,Errors,UpdateReq); {unhandled_ie} -> %Error, abort decoding {fault,193,UpdateReq} %Invalid message format end. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% decode_ie_delete_req/4 %%% Decode information elements for Delete PDP Context Request %%% Private Extension, Optional %%% Not implemented %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% decode_ie_delete_res/4 %%% Decode information elements for Delete PDP Context Response %%% All elements decoded decode_ie_delete_res(<<>>,PresentIEs,DeleteRes) -> %% Check mandatory IE's if (PresentIEs band 16#0001) =/= 16#0001 -> {fault,202,DeleteRes}; %Mandatory IE missing true -> %OK {ok,DeleteRes} end; %%% Cause, Mandatory decode_ie_delete_res(<<1:8,Cause:8,Rest/binary>>,PresentIEs,DeleteRes) -> if (PresentIEs band 16#00000001) =:= 16#00000001 -> %Repeated IE, ignored decode_ie_delete_res(Rest,PresentIEs,DeleteRes); true -> %OK UpdatedDeleteRes=DeleteRes#sesT_deleteResV0{cause=Cause}, decode_ie_delete_res(Rest,(PresentIEs bor 16#00000001), UpdatedDeleteRes) end; %%% Private Extension, Optional %%% Not implemented %%% Error handling, Unexpected or unknown IE decode_ie_delete_res(UnexpectedIE,PresentIEs,DeleteRes) -> case check_ie(UnexpectedIE) of {defined_ie,Rest} -> %OK, ignored decode_ie_delete_res(Rest,PresentIEs,DeleteRes); {handled_ie,Rest} -> %OK, ignored decode_ie_delete_res(Rest,PresentIEs,DeleteRes); {unhandled_ie} -> %Error, abort decoding {fault,193,DeleteRes} %Invalid message format end. %%% -------------------------------------------------------------- %%% #3.2 COMMON INTERNAL FUNCTIONS %%% -------------------------------------------------------------- %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% check_ie/1 %%% Check Information Element, Unexpected or Unknown check_ie(<<1:8,_:8,Rest/binary>>) -> {defined_ie,Rest}; %%% IMSI check_ie(<<2:8,_:8/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% RAI check_ie(<<3:8,_:6/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% TTLI check_ie(<<4:8,_:4/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% P-TMSI check_ie(<<5:8,_:4/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% Quality of Service Profile check_ie(<<6:8,_:3/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% Reordering Required check_ie(<<8:8,_:8,Rest/binary>>) -> {defined_ie,Rest}; %%% Authentication Triplet check_ie(<<9:8,_:28/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% MAP Cause check_ie(<<11:8,_:8,Rest/binary>>) -> {defined_ie,Rest}; %%% P-TMSI Signature check_ie(<<12:8,_:3/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% MS Validated check_ie(<<13:8,_:8,Rest/binary>>) -> {defined_ie,Rest}; %%% Recovery check_ie(<<14:8,_:8,Rest/binary>>) -> {defined_ie,Rest}; %%% Selection Mode check_ie(<<15:8,_:8,Rest/binary>>) -> {defined_ie,Rest}; %%% Flow Label Data I check_ie(<<16:8,_:16,Rest/binary>>) -> {defined_ie,Rest}; %%% Flow Label Signalling check_ie(<<17:8,_:16,Rest/binary>>) -> {defined_ie,Rest}; %%% Flow Label Data II check_ie(<<18:8,_:32,Rest/binary>>) -> {defined_ie,Rest}; %%% MS Not Reachable Reason check_ie(<<19:8,_:8,Rest/binary>>) -> {defined_ie,Rest}; %%% Charging ID check_ie(<<127:8,_:4/binary-unit:8,Rest/binary>>) -> {defined_ie,Rest}; %%% TLV element, skipped using Length check_ie(<<1:1,_:7,Length:16,More/binary>>) -> if Length > byte_size(More) -> {unhandled_ie}; true -> <<_:Length/binary-unit:8,Rest/binary>> = More, {handled_ie,Rest} end; %%% TV element, unknown size. Can not be handled. check_ie(_UnhandledIE) -> {unhandled_ie}. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% tid_internal_storage/3 %%% Convert TID binary to internal datatype tid_internal_storage(Bin,_) -> Size = byte_size(Bin) - 1, <> = Bin, Result = case DigitN of 2#1111 -> #mvsgT_tid{imsi = #mvsgT_imsi{value = Front}, nsapi = NSAPI}; _ -> Value = <>, #mvsgT_tid{imsi = #mvsgT_imsi{value = Value}, nsapi = NSAPI} end, {ok,Result}. %% tid_internal_storage(<>,IMSI) -> %% {ok,#mvsgT_tid{imsi=#mvsgT_imsi{value=lists:reverse(IMSI)}, %% nsapi=NSAPI}}; %% tid_internal_storage(<>,IMSI) when %% DigitN < 10 -> %% {ok,#mvsgT_tid{imsi=#mvsgT_imsi{value=lists:reverse([(DigitN bor 2#11110000)|IMSI])}, %% nsapi=NSAPI}}; %% tid_internal_storage(<<2#11111111:8,Rest/binary>>,IMSI) -> %% tid_internal_storage(Rest,IMSI); %% tid_internal_storage(<<2#1111:4,DigitN:4,Rest/binary>>,IMSI) when %% DigitN < 10 -> %% tid_internal_storage(Rest,[(DigitN bor 2#11110000)|IMSI]); %% tid_internal_storage(<>,IMSI) when %% DigitNplus1 < 10, %% DigitN < 10 -> %% tid_internal_storage(Rest,[((DigitNplus1 bsl 4) bor DigitN)|IMSI]); %% tid_internal_storage(_Rest,_IMSI) -> %% {fault}. %% Mandatory IE incorrect %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% selection_mode_internal_storage/1 %%% Convert Selection Mode integer to internal datatype (enum) selection_mode_internal_storage(0) -> subscribed; selection_mode_internal_storage(1) -> msRequested; selection_mode_internal_storage(2) -> sgsnSelected; selection_mode_internal_storage(3) -> sgsnSelected. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% pdp_addr_internal_storage/1 %%% Convert PDP address to internal datatype (record containing %%% addresstype and value) pdp_addr_internal_storage(<<_:4,0:4,1:8>>) -> {ok,#mvsgT_pdpAddressType{pdpTypeNbr=etsi_ppp,address=[]}}; pdp_addr_internal_storage(<<_:4,0:4,2:8>>) -> {ok,#mvsgT_pdpAddressType{pdpTypeNbr=etsi_osp_ihoss,address=[]}}; pdp_addr_internal_storage(<<_:4,1:4,16#21:8>>) -> {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv4,address=[]}}; pdp_addr_internal_storage(<<_:4,1:4,16#21:8,IP_A:8,IP_B:8,IP_C:8,IP_D:8>>) -> {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv4, address=[IP_A,IP_B,IP_C,IP_D]}}; pdp_addr_internal_storage(<<_:4,1:4,16#57:8,IP_A:16,IP_B:16,IP_C:16,IP_D:16, IP_E:16,IP_F:16,IP_G:16,IP_H:16>>) -> {ok,#mvsgT_pdpAddressType{pdpTypeNbr=ietf_ipv6, address=[IP_A,IP_B,IP_C,IP_D,IP_E,IP_F,IP_G,IP_H]}}; pdp_addr_internal_storage(_PDP_ADDR) -> {fault}. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% apn_internal_storage/2 %%% Convert APN to internal datatype (List containing APN labels) apn_internal_storage(<<>>,APN) -> {ok,lists:reverse(APN)}; apn_internal_storage(<>,APN) -> <> = Rest, apn_internal_storage(MoreAPNLabels,[Label|APN]). %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% pco_internal_storage/1 %%% Convert Protocol Configuration Options to internal datatype. %%% Implemented configuration options: %%% For PPP: %%% LCP - Not implemented %%% PAP - Authenticate request %%% CHAP - Challenge %%% - Response %%% IPCP - IP-Address %%% For OSP:IHOSS %%% Nothing implemented pco_internal_storage(<<1:1,_:4,0:3,PPPConfigurationOptions/binary>>) -> case ppp_configuration_options(PPPConfigurationOptions, #masT_pap{exists=false},[],[]) of {ok,PAP,CHAP,IPCP} -> {ok,#masT_protocolConfigOptions{pap=PAP,chap=CHAP,ipcp=IPCP}}; {fault} -> {fault} end; pco_internal_storage(<<1:1,_:4,1:3,_OSP_IHOSSConfigurationOptions/binary>>) -> {ok,osp_ihoss}; pco_internal_storage(_UnknownConfigurationOptions) -> {fault}. %% Optional IE incorrect ppp_configuration_options(<<>>,PAP,CHAP,IPCP) -> {ok,PAP,CHAP,IPCP}; ppp_configuration_options(<<16#C021:16,Length:8,More/binary>>,PAP,CHAP,IPCP) -> %% LCP - Not implemented <<_LCP:Length/binary-unit:8,Rest/binary>> = More, ppp_configuration_options(Rest,PAP,CHAP,IPCP); ppp_configuration_options(<<16#C023:16,_Length:8,1:8,Identifier:8,DataLength:16, More/binary>>,_PAP,CHAP,IPCP) -> %% PAP - Authenticate request ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself <> = More, <> = Data, <> = PeerData, <> = PasswordData, ppp_configuration_options(Rest,#masT_pap{exists=true,code=1,id=Identifier, username=binary_to_list(PeerID), password=binary_to_list(Password)},CHAP,IPCP); ppp_configuration_options(<<16#C023:16,Length:8,More/binary>>,PAP,CHAP,IPCP) -> %% PAP - Other, not implemented <<_PAP:Length/binary-unit:8,Rest/binary>> = More, ppp_configuration_options(Rest,PAP,CHAP,IPCP); ppp_configuration_options(<<16#C223:16,_Length:8,1:8,Identifier:8,DataLength:16, More/binary>>,PAP,CHAP,IPCP) -> %% CHAP - Challenge ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself <> = More, <> = Data, <> = ValueAndName, ppp_configuration_options(Rest,PAP,[#masT_chap{code=1,id=Identifier, value=binary_to_list(Value), name=binary_to_list(Name)}|CHAP], IPCP); ppp_configuration_options(<<16#C223:16,_Length:8,2:8,Identifier:8,DataLength:16, More/binary>>,PAP,CHAP,IPCP) -> %% CHAP - Response ActualDataLength=DataLength-4, %% DataLength includes Code, Identifier and itself <> = More, <> = Data, <> = ValueAndName, ppp_configuration_options(Rest,PAP,[#masT_chap{code=2,id=Identifier, value=binary_to_list(Value), name=binary_to_list(Name)}|CHAP], IPCP); ppp_configuration_options(<<16#C223:16,Length:8,More/binary>>,PAP,CHAP,IPCP) -> %% CHAP - Other, not implemented <<_CHAP:Length/binary-unit:8,Rest/binary>> = More, ppp_configuration_options(Rest,PAP,CHAP,IPCP); ppp_configuration_options(<<16#8021:16,_Length:8,1:8,Identifier:8,OptionsLength:16, More/binary>>,PAP,CHAP,IPCP) -> %% IPCP - Configure request ActualOptionsLength=OptionsLength-4, %% OptionsLength includes Code, Identifier and itself <> = More, case Options of <<3:8,6:8,A1:8,A2:8,A3:8,A4:8>> -> %% IP Address, version 4 ppp_configuration_options(Rest,PAP,CHAP, [#masT_ipcp{exists=true,code=1, id=Identifier, ipcpList=[#masT_ipcpData{type=3,ipAddress= #mvsgT_ipAddress{version=ipv4, a1=A1,a2=A2, a3=A3,a4=A4, a5=0,a6=0, a7=0,a8=0}, rawMessage=binary_to_list(Options)}]}|IPCP]); <<129:8,6:8,B1:8,B2:8,B3:8,B4:8>> -> %% IP Address, version 4 ppp_configuration_options(Rest,PAP,CHAP, [#masT_ipcp{exists=true,code=1, id=Identifier, ipcpList=[#masT_ipcpData{type=129,ipAddress= #mvsgT_ipAddress{version=ipv4, a1=B1,a2=B2, a3=B3,a4=B4}, rawMessage=binary_to_list(Options)}]}|IPCP]); <<131:8,6:8,C1:8,C2:8,C3:8,C4:8>> -> %% IP Address, version 4 ppp_configuration_options(Rest,PAP,CHAP, [#masT_ipcp{exists=true,code=1, id=Identifier, ipcpList=[#masT_ipcpData{type=131,ipAddress= #mvsgT_ipAddress{version=ipv4, a1=C1,a2=C2, a3=C3,a4=C4}, rawMessage=binary_to_list(Options)}]}|IPCP]); _ -> ppp_configuration_options(Rest,PAP,CHAP,IPCP) end; ppp_configuration_options(<<_UnknownProtocolID:16,Length:8,More/binary>>, PAP,CHAP,IPCP) -> <<_Skipped:Length/binary-unit:8,Rest/binary>> = More, ppp_configuration_options(Rest,PAP,CHAP,IPCP); ppp_configuration_options(_Unhandled,_PAP,_CHAP,_IPCP) -> {fault}. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% gsn_addr_internal_storage/1 %%% Convert GSN Address to internal datatype gsn_addr_internal_storage(<>) -> {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}}; gsn_addr_internal_storage(<>) -> {ok,#mvsgT_ipAddress{version=ipv6,a1=IP_A,a2=IP_B,a3=IP_C,a4=IP_D, a5=IP_E,a6=IP_F,a7=IP_G,a8=IP_H}}; gsn_addr_internal_storage(_GSN_ADDR) -> {fault}. %%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %%% msisdn_internal_storage/3 %%% Convert MSISDN binary to internal datatype (TBCD-octet list) msisdn_internal_storage(<<>>,MSISDN) -> {ok,#mvsT_msisdn{value=lists:reverse(MSISDN)}}; msisdn_internal_storage(<<2#11111111:8,_Rest/binary>>,MSISDN) -> {ok,#mvsT_msisdn{value=lists:reverse(MSISDN)}}; msisdn_internal_storage(<<2#1111:4,DigitN:4,_Rest/binary>>,MSISDN) when DigitN < 10 -> {ok,#mvsT_msisdn{value=lists:reverse([(DigitN bor 2#11110000)|MSISDN])}}; msisdn_internal_storage(<>,MSISDN) when DigitNplus1 < 10, DigitN < 10 -> NewMSISDN=[((DigitNplus1 bsl 4) bor DigitN)|MSISDN], msisdn_internal_storage(Rest,NewMSISDN); msisdn_internal_storage(_Rest,_MSISDN) -> {fault}. %% Mandatory IE incorrect