From 42333bf5fd87c4081539903f57e5d38667e6f8a4 Mon Sep 17 00:00:00 2001 From: mitshell Date: Sun, 11 Oct 2026 15:06:54 +0200 Subject: [PATCH 1/2] mobile: fix and rework the 5G URSP policy IEs --- pycrate_mobile/TS24501_UEPOL.py | 142 +++++-- pycrate_mobile/TS24526_UEPOL.py | 662 ++++++++++++++++++++++---------- 2 files changed, 566 insertions(+), 238 deletions(-) diff --git a/pycrate_mobile/TS24501_UEPOL.py b/pycrate_mobile/TS24501_UEPOL.py index 5b554d7..74d560d 100644 --- a/pycrate_mobile/TS24501_UEPOL.py +++ b/pycrate_mobile/TS24501_UEPOL.py @@ -44,7 +44,7 @@ from .TS24526_UEPOL import * from .TS24588_UEPOL import * from .TS24008_IE import ( - PLMN, + PLMN, PLMNList, ) @@ -82,6 +82,8 @@ class FGUEPOLHeader(Envelope): 2 : 'ANDSP', 3 : 'V2XP', 4 : 'ProSeP', + 5 : 'A2XP', + 6 : 'RSLPP', } @@ -93,7 +95,7 @@ class UEPolPart(Envelope): Alt('Cont', GEN={ 1 : URSPRules(), 2 : ANDSPInfos(), - 3 : V2XPInfos() + 3 : V2XPInfos(), # TODO: ProSePInfos(), see TS 24.555 }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), @@ -102,11 +104,11 @@ class UEPolPart(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: self[3].get_len()) - self[3].set_blauto(lambda: self[0].get_val()<<3) + self[0].set_valauto(lambda: self[3].get_len()+1) + self[3].set_blauto(lambda: (self[0].get_val()-1)<<3) -class UEPolInstruction(Envelope): +class UEPolInstr(Envelope): _GEN = ( Uint16('Len'), Uint16('UPSC', rep=REPR_HEX), @@ -115,15 +117,15 @@ class UEPolInstruction(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: self[2].get_len()) - self[2].set_blauto(lambda: self[0].get_val()<<3) + self[0].set_valauto(lambda: self[2].get_len()+2) + self[2].set_blauto(lambda: (self[0].get_val()-2)<<3) class UEPolSectionSublist(Envelope): _GEN = ( Uint16('Len'), PLMN(), - Sequence('Cont', GEN=UEPolInstruction()) + Sequence('Cont', GEN=UEPolInstr()) ) def __init__(self, *args, **kwargs): @@ -149,7 +151,7 @@ class UEPolResult(Envelope): ) -class UEPolSectionMgmtSubresult(Envelope): +class UEPolSectionSubresult(Envelope): _GEN = ( Uint8('Num'), PLMN(), @@ -162,8 +164,8 @@ def __init__(self, *args, **kwargs): self[2].set_numauto(lambda: self[0].get_val()) -class UEPolSectionMgmtResult(Sequence): - _GEN = UEPolSectionMgmtSubresult() +class UEPolSectionResult(Sequence): + _GEN = UEPolSectionSubresult() #------------------------------------------------------------------------------# @@ -193,7 +195,7 @@ class UPSIList(Sequence): # TS 24.501, section D.6.5 #------------------------------------------------------------------------------# -class UEPolicyCm(Envelope): +class UEPolCm(Envelope): _GEN = ( Uint('spare', bl=7), Uint('ANDSPSupp', bl=1), @@ -231,7 +233,7 @@ class UEOSId(Array): # TS 24.501, section D.6.7 #------------------------------------------------------------------------------# -class UEPolicyNetCm(Envelope): +class UEPolNetCm(Envelope): _GEN = ( Uint('spare', bl=7), Uint('NSSUI', bl=1), @@ -255,17 +257,104 @@ def enable_upto(self, ind): [e.set_trans(False) for e in self._content[:ind]] +#------------------------------------------------------------------------------# +# VPS URSP configuration +# TS 24.501, section D.6.8 +#------------------------------------------------------------------------------# + +class _VPSURSPConfigVPLMNs(Envelope): + _GEN = ( + Uint8('Num'), + PLMNList() + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self[0].set_valauto(lambda: self[1].get_num()) + self[1].set_numauto(lambda: self[0].get_val()) + + def decode(self): + return [self['Num'].get_val(), self['PLMNList'].decode()] + + +class _VPSURSPConfigMCCs(Envelope): + _GEN = ( + Uint8('Num'), + Buf('Values', rep=REPR_HEX) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self[0].set_valauto(lambda: self._get_num_mccs()) + self[1].set_blauto(lambda: self._get_len_mccs()) + + def _get_num_mccs(self): + mccs_len = self['Values'].get_len() + num, rest = mccs_len // 3, mccs_len % 3 + if rest: + # odd number of MCCs + num += 1 + return num + + def _get_len_mccs(self): + mccs_num = self['Num'].get_val() + if mccs_num % 2: + # odd number of MCCs + return ((mccs_num//2)*3 + 2)<<3 + else: + return ((mccs_num//2)*3)<<3 + + # Note: we're missing proper encoding / decoding for those nested MCC pairs + + +class _VPSURSPConfigNetDesc(Envelope): + _GEN = ( + Uint8('Type', val=2, dic={1: 'one or more VPLMNs', 2: 'one or more MCCs', 3: 'any VPLMN'}), + Alt('Entries', GEN={ + 1 : _VPSURSPConfigVPLMNs('VPLMNs'), + 2 : _VPSURSPConfigMCCs('MCCs'), + 3 : Buf('none', bl=0), + }, + DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), + sel=lambda self: self.get_env()['Type'].get_val()) + ) + + +class _VPSURSPConfigTuple(Envelope): + _GEN = ( + Uint16('Len'), + Uint8('ID'), + Uint8('NumNetDescs'), + Sequence('NetDescs', GEN=_VPSURSPConfigNetDesc('NetDesc')) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Len'].set_valauto(lambda: self['NetDescs'].get_len() + 2) + self['NumNetDescs'].set_valauto(lambda: self['NetDescs'].get_num()) + self['NetDescs'].set_numauto(lambda: self['NumNetDescs'].get_val()) + + +class VPSURSPConfig(Envelope): + _GEN = ( + Uint('spare', bl=6, rep=REPR_HEX), + Uint('RT', val=1, bl=2, dic={1: 'per-tuple replacement', 2: 'full list of tuples'}), + Sequence('Tuples', GEN=_VPSURSPConfigTuple('Tuple')) + ) + + #------------------------------------------------------------------------------# # Manage UE policy command # TS 24.501, section D.5.1 #------------------------------------------------------------------------------# -class FGUEPOLManageUEPolicyCommand(Layer3): - _name = '5GUEPOLManageUEPolicyCommand' +class FGUEPOLManageUEPolCmd(Layer3): + _name = '5GUEPOLManageUEPolCmd' _GEN = ( FGUEPOLHeader(val={'Type':1}), - Type6LVE('UEPolSectionList', val={'V':b'\0\7\0\xf1\x10\0\3\0\0\0\0\0'}, IE=UEPolSectionList()), - Type4TLV('UEPolicyCm', val={'T':0x42, 'V':b'\0'}, IE=UEPolicyNetCm()), + Type6LVE('UEPolSectionList', val={'V':b'\0\x09\0\7\0\xf1\x10\0\2\0\0'}, IE=UEPolSectionList()), + Type4TLV('UEPolNetCm', val={'T':0x42, 'V':b'\0'}, IE=UEPolNetCm()), + Type6TLVE('VPSURSPConfig', val={'T': 0x70, 'V':b'\1' }, IE=VPSURSPConfig()) ) @@ -274,8 +363,8 @@ class FGUEPOLManageUEPolicyCommand(Layer3): # TS 24.501, section D.5.2 #------------------------------------------------------------------------------# -class FGUEPOLManageUEPolicyComplete(Layer3): - _name = '5GUEPOLManageUEPolicyComplete' +class FGUEPOLManageUEPolCmpl(Layer3): + _name = '5GUEPOLManageUEPolCmpl' _GEN = ( FGUEPOLHeader(val={'Type':2}), ) @@ -286,11 +375,11 @@ class FGUEPOLManageUEPolicyComplete(Layer3): # TS 24.501, section D.5.3 #------------------------------------------------------------------------------# -class FGUEPOLManageUEPolicyCommandReject(Layer3): - _name = '5GUEPOLManageUEPolicyCommandReject' +class FGUEPOLManageUEPolRej(Layer3): + _name = '5GUEPOLManageUEPolRej' _GEN = ( FGUEPOLHeader(val={'Type':3}), - Type6LVE('UEPolSectionMgmtResult', val={'V':b'\1\0\xf1\x10\0\0\0\0\x6f'}, IE=UEPolSectionMgmtResult()), + Type6LVE('UEPolSectionResult', val={'V':b'\1\0\xf1\x10\0\0\0\0\x6f'}, IE=UEPolSectionResult()), ) @@ -304,7 +393,7 @@ class FGUEPOLStateInd(Layer3): _GEN = ( FGUEPOLHeader(val={'Type':4}), Type6LVE('UPSIList', val={'V':b'\0\5\0\xf1\x10\0\0'}, IE=UPSIList()), - Type4LV('UEPolicyCm', val={'V':b'\0'}, IE=UEPolicyCm()), + Type4LV('UEPolCm', val={'V':b'\0'}, IE=UEPolCm()), Type4TLV('UEOSId', val={'T':0x41, 'V':16*b'\0'}, IE=UEOSId()) ) @@ -314,12 +403,11 @@ class FGUEPOLStateInd(Layer3): #------------------------------------------------------------------------------# FGUEPOLTypeClasses = { - 1 : FGUEPOLManageUEPolicyCommand, - 2 : FGUEPOLManageUEPolicyComplete, - 3 : FGUEPOLManageUEPolicyCommandReject, + 1 : FGUEPOLManageUEPolCmd, + 2 : FGUEPOLManageUEPolCmpl, + 3 : FGUEPOLManageUEPolRej, 4 : FGUEPOLStateInd } def get_5guepol_msg_instances(): return {k: FGUEPOLTypeClasses[k]() for k in FGUEPOLTypeClasses} - diff --git a/pycrate_mobile/TS24526_UEPOL.py b/pycrate_mobile/TS24526_UEPOL.py index 5038397..c157e71 100644 --- a/pycrate_mobile/TS24526_UEPOL.py +++ b/pycrate_mobile/TS24526_UEPOL.py @@ -30,10 +30,9 @@ __all__ = [ 'URSPRules', 'URSPRule', - 'ANDSPInfos', - 'ANDSPInfo', - 'N3AN', 'WLANSPRule', + 'ANDSPInfos', + 'ANDSPInfo' ] #------------------------------------------------------------------------------# @@ -45,14 +44,13 @@ from pycrate_core.elt import * from pycrate_core.base import * -from pycrate_ether.IP import IPProt_dict +from pycrate_ether.IP import IPAddr, IPProt_dict from pycrate_ether.Ethernet import EtherType_dict -from .TS24008_IE import ( - PLMN, - ) -from .TS24501_IE import ( - DNN, _SSCMode_dict, SNSSAI, PDUSessType, FGSTAIList, +from pycrate_mobile.TS29244_PFCP import FQDN +from pycrate_mobile.TS24008_IE import PLMN +from pycrate_mobile.TS24501_IE import ( + DNN, _SSCMode_dict, NSSAI, L_SNSSAI, PDUSESSTYPE, _PDUSessType_dict, FGSTAIList, ) @@ -63,41 +61,54 @@ # Table 5.2.1, Traffic descriptor component type identifier _TrafficDescCompType_dict = { - 1 : 'Match-all type', - 8 : 'OS Id + OS App Id type', - 16 : 'IPv4 remote address type', - 17 : 'IPv4 local address type', - 33 : 'IPv6 remote address/prefix length type', - 35 : 'IPv6 local address/prefix length type', - 48 : 'Protocol identifier/Next header type', - 64 : 'Single local port type', - 65 : 'Local port range type', - 80 : 'Single remote port type', - 81 : 'Remote port range type', - 96 : 'Security parameter index type', - 112 : 'Type of service/Traffic class type', - 128 : 'Flow label type', - 129 : 'Destination MAC address type', - 130 : 'Source MAC address type', - 131 : '802.1Q C-TAG VID type', - 132 : '802.1Q S-TAG VID type', - 133 : '802.1Q C-TAG PCP/DEI type', - 134 : '802.1Q S-TAG PCP/DEI type', - 135 : 'Ethertype type', - 136 : 'DNN type', - 144 : 'Connection capabilities type', + 1 : 'Match-all', + 8 : 'OS Id + OS App Id', + 16 : 'IPv4 remote address', + 33 : 'IPv6 remote address/prefix length', + 48 : 'Protocol identifier/next header', + 80 : 'Single remote port', + 81 : 'Remote port range', + 82 : 'IP 3-tuple', + 96 : 'Security parameter index', + 112 : 'Type of service/traffic class', + 128 : 'Flow label', + 129 : 'Destination MAC address', + 131 : '802.1Q C-TAG VID', + 132 : '802.1Q S-TAG VID', + 133 : '802.1Q C-TAG PCP/DEI', + 134 : '802.1Q S-TAG PCP/DEI', + 135 : 'Ethertype', + 136 : 'DNN', + 144 : 'Connection capabilities', 145 : 'Destination FQDN', - 160 : 'OS App Id type' + 146 : 'Regular expression', + 160 : 'OS App Id', + 161 : 'Destination MAC address range', + 162 : 'PIN ID', + 163 : 'Connectivity group ID' } _TrafficDescConCap_dict = { - 1 : 'IMS', - 2 : 'MMS', - 4 : 'SUPL', - 8 : 'Internet' + 1 : 'IMS', + 2 : 'MMS', + 4 : 'SUPL', + 8 : 'Internet', + 16 : 'LCS user plane positioning', + 161 : 'IoT delay-tolerant', + 162 : 'IoT non-delay-tolerant', + 163 : 'Downlink streaming', + 164 : 'Uplink streaming', + 165 : 'Vehicular communications', + 166 : 'Real time interactive', + 167 : 'Unified communications', + 168 : 'Background', + 169 : 'Mission critical communications', + 170 : 'Time critical communications', + 171 : 'Low latency loss tolerant communications in unacknowledged mode' } +# Table 5.2.1, OS Id + OS App Id type class _TrafficDescCompOSAppId(Envelope): _GEN = ( Buf('OS_UUID', bl=128, rep=REPR_HEX), @@ -111,20 +122,23 @@ def __init__(self, *args, **kwargs): self[2].set_blauto(lambda: self[1].get_val()<<3) +# Table 5.2.1, IPv4 remote address type class _TrafficDescCompIPv4(Envelope): _GEN = ( - Buf('Addr', bl=32, rep=REPR_HEX), - Buf('Mask', bl=32, rep=REPR_HEX) + IPAddr('Addr', bl=32), + IPAddr('Mask', bl=32) ) +# Table 5.2.1, IPv6 remote address/prefix length type class _TrafficDescCompIPv6(Envelope): _GEN = ( - Buf('Addr', bl=128, rep=REPR_HEX), - Uint8('Pref') + IPAddr('Addr', bl=128), + Uint8('PrefLen') ) +# Table 5.2.1, remote port range type class _TrafficDescCompPortRange(Envelope): _GEN = ( Uint16('Low'), @@ -132,6 +146,38 @@ class _TrafficDescCompPortRange(Envelope): ) +# Table 5.2.1, IP 3 tuple type +class _TrafficDescCompIP3tuple(Envelope): + _GEN = ( + Uint('spare', bl=3), + Uint('BMPortRange', bl=1), + Uint('BMPort', bl=1), + Uint('BMProtId', bl=1), + Uint('BMIPv6Pref', bl=1), + Uint('BMIPv4', bl=1), + _TrafficDescCompIPv4('IPv4'), + _TrafficDescCompIPv6('IPv6Pref'), + Uint8('ProtId', dic=IPProt_dict), + Uint16('Port'), + _TrafficDescCompPortRange('PortRange'), + ) + + # Note on presence of subfields: + # - Port and PortRange are exclusive + # - IPv4 and IPv6Pref are exclusive + # - At least one subfield must be set + # In all other cases, the IP 3-tuple rule must be ignored by the receiver + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['IPv4'].set_transauto(lambda: self['BMIPv4'].get_val() == 0) + self['IPv6Pref'].set_transauto(lambda: self['BMIPv6Pref'].get_val() == 0) + self['ProtId'].set_transauto(lambda: self['BMProtId'].get_val() == 0) + self['Port'].set_transauto(lambda: self['BMPort'].get_val() == 0) + self['PortRange'].set_transauto(lambda: self['BMPortRange'].get_val() == 0) + + +# Table 5.2.1, type of service/traffic class type class _TrafficDescCompTrafficClass(Envelope): _GEN = ( Uint8('Class'), @@ -139,6 +185,7 @@ class _TrafficDescCompTrafficClass(Envelope): ) +# Table 5.2.1, flow label type class _TrafficDescCompFlowLabel(Envelope): _GEN = ( Uint('spare', bl=4), @@ -146,6 +193,7 @@ class _TrafficDescCompFlowLabel(Envelope): ) +# Table 5.2.1, 802.1Q C-TAG VID type / 802.1Q S-TAG VID type class _TrafficDescCompVID(Envelope): _GEN = ( Uint('spare', bl=4), @@ -153,6 +201,7 @@ class _TrafficDescCompVID(Envelope): ) +# Table 5.2.1, 802.1Q C-TAG PCP/DEI type / 802.1Q S-TAG PCP/DEI type class _TrafficDescCompPCPDEI(Envelope): _GEN = ( Uint('spare', bl=4), @@ -161,6 +210,7 @@ class _TrafficDescCompPCPDEI(Envelope): ) +# Table 5.2.1, connection capabilities type class _TrafficDescCompConCap(Envelope): _GEN = ( Uint8('Num'), @@ -173,10 +223,11 @@ def __init__(self, *args, **kwargs): self[1].set_numauto(lambda: self[0].get_val()) +# Table 5.2.1, destination FQDN type class _TrafficDescCompFQDN(Envelope): _GEN = ( Uint8('Len'), - Buf('Value', val=b'') + FQDN('Value') ) def __init__(self, *args, **kwargs): @@ -185,10 +236,11 @@ def __init__(self, *args, **kwargs): self[1].set_blauto(lambda: self[0].get_val()<<3) -class _TrafficDescCompAppId(Envelope): +# Table 5.2.1, regular expression type +class _TrafficDescCompRegExp(Envelope): _GEN = ( Uint8('Len'), - Buf('AppId') + String('Value') ) def __init__(self, *args, **kwargs): @@ -197,6 +249,28 @@ def __init__(self, *args, **kwargs): self[1].set_blauto(lambda: self[0].get_val()<<3) +# Table 5.2.1, OS App Id type / PIN ID type / connectivity group ID type +class _TrafficDescCompLV(Envelope): + _GEN = ( + Uint8('Len'), + Buf('Value') + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self[0].set_valauto(lambda: self[1].get_len()) + self[1].set_blauto(lambda: self[0].get_val()<<3) + + +# Table 5.2.1, destination MAC address range type +class _TrafficDescCompMACRange(Envelope): + _GEN = ( + Buf('Low', bl=48, rep=REPR_HEX), + Buf('High', bl=48, rep=REPR_HEX), + ) + + +# Table 5.2.1, class TrafficDescComp(Envelope): _GEN = ( Uint8('Type', dic=_TrafficDescCompType_dict), @@ -204,19 +278,15 @@ class TrafficDescComp(Envelope): 1 : Buf('none', bl=0), 8 : _TrafficDescCompOSAppId('OSAppId'), 16 : _TrafficDescCompIPv4('IPv4'), - 17 : _TrafficDescCompIPv4('IPv4'), 33 : _TrafficDescCompIPv6('IPv6Pref'), - 35 : _TrafficDescCompIPv6('IPv6Pref'), 48 : Uint8('ProtId', dic=IPProt_dict), - 64 : Uint16('Port'), - 65 : _TrafficDescCompPortRange('PortRange'), 80 : Uint16('Port'), 81 : _TrafficDescCompPortRange('PortRange'), + 82 : _TrafficDescCompIP3tuple('IP3tuple'), 96 : Uint32('SPI', rep=REPR_HEX), 112 : _TrafficDescCompTrafficClass('TrafficClass'), 128 : _TrafficDescCompFlowLabel('FlowLabel'), 129 : Buf('MACDest', bl=48, rep=REPR_HEX), - 130 : Buf('MACSrc', bl=48, rep=REPR_HEX), 131 : _TrafficDescCompVID('CTagVID'), 132 : _TrafficDescCompVID('STagVID'), 133 : _TrafficDescCompPCPDEI('CTagPCPDEI'), @@ -225,11 +295,19 @@ class TrafficDescComp(Envelope): 136 : DNN(), 144 : _TrafficDescCompConCap('ConCap'), 145 : _TrafficDescCompFQDN('FQDN'), - 160 : _TrafficDescCompAppId('AppId') + 146 : _TrafficDescCompRegExp('RegExp'), + 160 : _TrafficDescCompLV('AppId'), + 161 : _TrafficDescCompMACRange('MACRange'), + 162 : _TrafficDescCompLV('PINID'), + 163 : _TrafficDescCompLV('ConGroupID') }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), sel=lambda self: self.get_env()['Type'].get_val()) ) + + # Note: if the "match-all type" traffic descriptor component is included in a traffic descriptor, + # there shall be no traffic descriptor component with a type other than "match-all type" in the traffic descriptor, + # and it shall not appear more than once among all traffic descriptors of the whole URSP rules # Table 5.2.1, SSC Mode Type @@ -240,6 +318,14 @@ class _RouteSelectDescCompSSCMode(Envelope): ) +# Table 5.2.1, PDU session type type type +class _RouteSelectDescCompPDUSessType(Envelope): + _GEN = ( + Uint('spare', bl=5, rep=REPR_HEX), + Uint('Value', val=PDUSESSTYPE.IPV4, bl=3, dic=_PDUSessType_dict) + ) + + # Table 5.2.1, preferred access type type class _RouteSelectDescCompAccessType(Envelope): _GEN = ( @@ -249,15 +335,16 @@ class _RouteSelectDescCompAccessType(Envelope): # Table 5.2.1, time window type +# 64-bit NTP timestamps class _RouteSelectDescCompTimeWin(Envelope): _GEN = ( Envelope('StartTime', GEN=( - Uint32('Second'), - Uint32('Fraction') + Uint32('Sec'), + Uint32('Fract') )), Envelope('StopTime', GEN=( - Uint32('Second'), - Uint32('Fraction') + Uint32('Sec'), + Uint32('Fract') )) ) @@ -280,7 +367,7 @@ class _LocAreaCompEUtra(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) self[0].set_valauto(lambda: self[1].get_num()) - self[1].set_numauto(lambda: self[0].get_num()) + self[1].set_numauto(lambda: self[0].get_val()) class _LocAreaNRCellID(Envelope): @@ -300,29 +387,29 @@ class _LocAreaCompNR(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) self[0].set_valauto(lambda: self[1].get_num()) - self[1].set_numauto(lambda: self[0].get_num()) + self[1].set_numauto(lambda: self[0].get_val()) -class _LocAreaGNID(Envelope): +class _LocAreaGNBID(Envelope): _GEN = ( PLMN(), - Buf('gNBID', bl=32, rep=REPR_HEX) + Buf('GNBID', bl=32, rep=REPR_HEX) ) -class _LocAreaCompGNID(Envelope): +class _LocAreaCompGNBID(Envelope): _GEN = ( Uint8('Num'), - Sequence('gNBIDs', GEN=_LocAreaGNID('gNBID')) + Sequence('GNBIDs', GEN=_LocAreaGNBID('GNBID')) ) def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) self[0].set_valauto(lambda: self[1].get_num()) - self[1].set_numauto(lambda: self[0].get_num()) + self[1].set_numauto(lambda: self[0].get_val()) -# Figure 5.2.6: Location area +# Table 5.2.2: Location area _RouteSelectLocAreaType_dict = { 1 : 'E-UTRA cell identities list', 2 : 'NR cell identities list', @@ -335,9 +422,9 @@ class _LocAreaComp(Envelope): _GEN = ( Uint8('Type', dic=_RouteSelectLocAreaType_dict), Alt('Cont', GEN={ - 1 : _LocAreaCompEUtra('EUTRACellIDs'), - 2 : _LocAreaCompNR('NRCellIDs'), - 3 : _LocAreaCompGNID('gNBIDs'), + 1 : _LocAreaCompEUtra('EUTRACellIDList'), + 2 : _LocAreaCompNR('NRCellIDList'), + 3 : _LocAreaCompGNBID('GNBIDList'), 4 : FGSTAIList() }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), @@ -358,9 +445,13 @@ class _RouteSelectDescCompLocArea(Sequence): 8 : 'PDU session type', 16 : 'Preferred access type', 17 : 'Multi-access preference', - 128 : 'Time window', + 32 : 'Non-seamless non-3GPP offload indication', 64 : 'Location criteria', - 32 : 'Non-seamless non-3GPP offload indication' + 128 : 'Time window', + 129 : '5G ProSe layer-3 UE-to-network relay offload indication', + 130 : 'PDU session pair ID', + 131 : 'RSN', + 132 : '5G ProSe multi-path preference', } @@ -369,14 +460,18 @@ class RouteSelectDescComp(Envelope): Uint8('Type', dic=_RouteSelectDescCompType_dict), Alt('Value', GEN={ 1 : _RouteSelectDescCompSSCMode('SSCMode'), - 2 : SNSSAI(), + 2 : L_SNSSAI(), 4 : DNN(), - 8 : PDUSessType(), + 8 : _RouteSelectDescCompPDUSessType('PDUSessType'), 16 : _RouteSelectDescCompAccessType('AccessType'), 17 : Buf('none', bl=0), 32 : Buf('none', bl=0), 64 : _RouteSelectDescCompLocArea('LocArea'), - 128 : _RouteSelectDescCompTimeWin('TimeWin') + 128 : _RouteSelectDescCompTimeWin('TimeWin'), + 129 : Buf('none', bl=0), + 130 : Uint8('PDUSessPairID'), + 131 : Uint8('RSN'), + 132 : Buf('none', bl=0) }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), sel=lambda self: self.get_env()['Type'].get_val()) @@ -394,9 +489,9 @@ class RouteSelectDesc(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 3 + self[3].get_len()) - self[2].set_valauto(lambda: self[3].get_len()) - self[3].set_blauto(lambda: self[2].get_val()<<3) + self['Len'].set_valauto(lambda: 3 + self['Cont'].get_len()) + self['LenCont'].set_valauto(lambda: self['Cont'].get_len()) + self['Cont'].set_blauto(lambda: self['LenCont'].get_val()<<3) # Figure 5.2.2: URSP rule @@ -407,21 +502,29 @@ class URSPRule(Envelope): Uint16('LenTrafficDesc'), Sequence('TrafficDesc', GEN=TrafficDescComp()), Uint16('LenRouteSelectDescList'), - Sequence('RouteSelectDescList', GEN=RouteSelectDesc()) + Sequence('RouteSelectDescList', GEN=RouteSelectDesc()), + Buf('AddInd', val=b'', rep=REPR_HEX) # 0 or 1 byte (for now) ) def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 5 + self[3].get_len() + self[5].get_len()) - self[2].set_valauto(lambda: self[3].get_len()) - self[3].set_blauto(lambda: self[2].get_val()<<3) - self[4].set_valauto(lambda: self[5].get_len()) - self[5].set_blauto(lambda: self[4].get_val()<<3) + self['Len'].set_valauto(lambda: + 5 + self['TrafficDesc'].get_len() + self['RouteSelectDescList'].get_len() + self['AddInd'].get_len() + ) + self['LenTrafficDesc'].set_valauto(lambda: self['TrafficDesc'].get_len()) + self['TrafficDesc'].set_blauto(lambda: self['LenTrafficDesc'].get_val()<<3) + self['LenRouteSelectDescList'].set_valauto(lambda: self['RouteSelectDescList'].get_len()) + self['RouteSelectDescList'].set_blauto(lambda: self['LenRouteSelectDescList'].get_val()<<3) + self['AddInd'].set_blauto(lambda: + (self['Len'].get_val() - self['LenTrafficDesc'].get_val() - self['LenRouteSelectDescList'].get_val() - 5)<<3 + ) # Figure 5.2.1: UE policy part contents including one or more URSP rules class URSPRules(Sequence): _GEN = URSPRule() + + # Note: multiple URSP rules in the URSP shall not have the same precedence value #------------------------------------------------------------------------------# @@ -429,6 +532,18 @@ class URSPRules(Sequence): # TS 24.526, section 5.3 #------------------------------------------------------------------------------# +class SSID(Envelope): + _GEN = ( + Uint8('Len'), + Buf('Value') + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self[0].set_valauto(lambda: self[1].get_len()) + self[1].set_blauto(lambda: self[0].get_val()<<3) + + # Figure 5.3.2.4g: Selection criteria sub entry {selection criteria set type = minimum backhaul threshold} class _WLANSelectionCriteriaMinBack(Envelope): _GEN = ( @@ -455,7 +570,7 @@ class _WLANSelectionCriteriaSPExcl(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: self[1].get_val()) + self[0].set_valauto(lambda: self[1].get_len()) self[1].set_blauto(lambda: self[0].get_val()<<3) @@ -463,7 +578,7 @@ def __init__(self, *args, **kwargs): class _WLANSelectionCriteriaPortTuple(Envelope): _GEN = ( Uint8('Len'), - Uint8('PortId', dic=IPProt_dict), + Uint8('ProtId', dic=IPProt_dict), Uint8('LenPort'), Buf('Port', val=b'\0\1', rep=REPR_HEX), # see WiFi Alliance HotSpot 2.0 ) @@ -480,22 +595,23 @@ class _WLANSelectionCriteriaPrefRoam(Envelope): _GEN = ( Uint8('Len'), Uint8('Priority'), - Uint8('LenFQDN'), - Buf('FQDNMatch', rep=REPR_HEX), # see WiFi Alliance HotSpot 2.0 + Uint8('LenFQDN_Match'), + Buf('FQDN_Match', rep=REPR_HEX), # see WiFi Alliance HotSpot 2.0 Uint8('LenCountry'), Buf('Country', rep=REPR_HEX), # see WiFi Alliance HotSpot 2.0 ) def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 3 + self['LenFQDN'].get_val() + self['LenCountry'].get_val()) - self[2].set_valauto(lambda: self[3].get_len()) - self[3].set_blauto(lambda: self[2].get_val()<<3) - self[4].set_valauto(lambda: self[5].get_len()) - self[5].set_blauto(lambda: self[4].get_val()<<3) + self['Len'].set_valauto(lambda: 3 + self['FQDN_Match'].get_len() + self['Country'].get_len()) + self['LenFQDN_Match'].set_valauto(lambda: self['FQDN_Match'].get_len()) + self['FQDN_Match'].set_blauto(lambda: self['LenFQDN_Match'].get_val()<<3) + self['LenCountry'].set_valauto(lambda: self['Country'].get_len()) + self['Country'].set_blauto(lambda: self['LenCountry'].get_val()<<3) + +# Figure 5.3.2.4c: Selection criteria sub entry {selection criteria set type = preferred SSID list} -# Figure 5.3.2.4c: Selection criteria sub entry {selection criteria set type = preferred SSID list} class _WLANSelectionCriteriaPrefSSID(Envelope): _GEN = ( Uint8('Len'), @@ -503,21 +619,47 @@ class _WLANSelectionCriteriaPrefSSID(Envelope): Uint('spare', bl=6), Uint('HESSIDInd', bl=1), Uint('SSIDInd', bl=1), - Envelope('SSID', GEN=( - Uint8('Len'), - Buf('Value', val=b'', rep=REPR_HEX)) - ), + SSID(), Buf('HESSID', bl=48, rep=REPR_HEX) # MAC addr ) def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 2 + self[5].get_len() + self[6].get_len()) + self['Len'].set_valauto(lambda: 2 + self['SSID'].get_len() + self['HESSID'].get_len()) self['SSID'].set_transauto(lambda: self['SSIDInd'].get_val() == 0) self['HESSID'].set_transauto(lambda: self['HESSIDInd'].get_val() == 0) - # SSID internal automation - self['SSID'][0].set_valauto(lambda: self['SSID'][1].get_len()) - self['SSID'][1].set_blauto(lambda: self['SSID'][0].get_val()<<3) + + +# Figure 5.3.2.21: SSID list +class SSIDList(Envelope): + _GEN = ( + Uint8('Num'), + Sequence('SSIDList', GEN=SSID()), + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Num'].set_valauto(lambda: self['SSIDList'].get_num()) + self['SSIDList'].set_numauto(lambda: self['Num'].get_val()) + + +# Figure 5.3.2.4h:Selection criteria sub entry {selection criteria set type = slice-based TNAN list} +class _WLANSelectionCriteriaTNAN(Envelope): + _GEN = ( + Uint8('Len'), + Uint8('LenTNGFID'), + Buf('TNGFID', val=b''), + NSSAI(), + SSIDList() + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Len'].set_valauto(lambda: + 1 + self['TNGFID'].get_len() + self['NSSAI'].get_len() + self['SSIDList'].get_len() + ) + self['LenTNGFID'].set_valauto(lambda: self['TNGFID'].get_len()) + self['TNGFID'].set_blauto(lambda: self['LenTNGFID'].get_val()<<3) # Figure 5.3.2.4b @@ -526,7 +668,8 @@ def __init__(self, *args, **kwargs): 2 : 'preferred roaming partner list', 3 : 'required protocol port tuple', 4 : 'SP exclusion list', - 5 : 'minimum backhaul threshold' + 5 : 'minimum backhaul threshold', + 6 : 'slice-based TNAN list' } @@ -537,20 +680,21 @@ class _WLANSelectionCriteriaSet(Envelope): Uint('Num', bl=4), Sequence('SubEntries', GEN=Alt('CriteriaSubEntry', GEN={ 1 : _WLANSelectionCriteriaPrefSSID('PrefSSID'), - 2 : _WLANSelectionCriteriaPrefRoam('PrefRoaming'), - 3 : _WLANSelectionCriteriaPortTuple('RequiredProtocolPort'), + 2 : _WLANSelectionCriteriaPrefRoam('PrefRoam'), + 3 : _WLANSelectionCriteriaPortTuple('RequiredProtPort'), 4 : _WLANSelectionCriteriaSPExcl('SPExcl'), - 5 : _WLANSelectionCriteriaMinBack('MinBackhaulThres')}, + 5 : _WLANSelectionCriteriaMinBack('MinBackhaulThres'), + 6 : _WLANSelectionCriteriaTNAN('TNAN')}, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), sel=lambda self: self.get_env().get_env()['Type'].get_val())) ) def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 1 + self[3].get_len()) - self[2].set_valauto(lambda: self[3].get_num()) - self[3].set_numauto(lambda: self[2].get_val()) - self[3].set_blauto(lambda: (self[0].get_val()-1)<<3) + self['Len'].set_valauto(lambda: 1 + self['SubEntries'].get_len()) + self['Num'].set_valauto(lambda: self['SubEntries'].get_num()) + self['SubEntries'].set_numauto(lambda: self['Num'].get_val()) + self['SubEntries'].set_blauto(lambda: (self['Len'].get_val() - 1)<<3) # Table 5.3.2.1: WLANSP information element, Home network ind @@ -560,7 +704,7 @@ def __init__(self, *args, **kwargs): } -# Figure 5.3.2.4a +# Figure 5.3.2.4a class _WLANSelectionCriteriaEntry(Envelope): _GEN = ( Uint16('Len'), @@ -574,9 +718,15 @@ class _WLANSelectionCriteriaEntry(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 3 + self[6].get_len() if self[2].get_val() == 1 else 1 + self[6].get_len()) - self[5].set_transauto(lambda: self[2].get_val() == 0) - self[6].set_blauto(lambda: ((self[0].get_val()-3)<<3) if self[2].get_val() == 1 else ((self[0].get_val()-1)<<3)) + self['Len'].set_valauto(lambda: + 3 + self['CriteriaSets'].get_len() if self['MaxBSSLoadInd'].get_val() == 1 else \ + 1 + self['CriteriaSets'].get_len() + ) + self['MaxBSSLoad'].set_transauto(lambda: self['MaxBSSLoadInd'].get_val() == 0) + self['CriteriaSets'].set_blauto(lambda: + (self['Len'].get_val() - 3)<<3 if self['MaxBSSLoadInd'].get_val() == 1 else \ + (self['Len'].get_val() - 1)<<3 + ) # Figure 5.3.2.4 @@ -594,11 +744,36 @@ def __init__(self, *args, **kwargs): self[2].set_numauto(lambda: self[1].get_val()) -# Figure 5.3.2.11c: Location field {entry type= WLAN location} +# Figure 5.3.2.11a: Location field {entry type= Geo location} +class _LocFieldGeo(Envelope): + _GEN = ( + Uint32('Lat'), + Uint32('Long'), + Uint16('Radius') + ) + + +# Figure 5.3.2.10a: Location sub entry {entry type= WLAN location or Geo location} +class _LocSubentryGeo(Envelope): + _GEN = ( + Uint16('Len'), + Uint8('Num'), + Sequence('Fields', GEN=_LocFieldGeo('LocGeo')) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self[0].set_valauto(lambda: 1 + self[2].get_len()) + self[1].set_valauto(lambda: self[2].get_num()) + self[2].set_numauto(lambda: self[1].get_val()) + self[2].set_blauto(lambda: (self[0].get_val() - 1)<<3) + + +# Figure 5.3.2.11c: Location field {entry type= WLAN location} _LocFieldType_dict = { 1 : 'TAC', - 2 : 'EUTRA CellID', - 4 : 'NR CellID', + 2 : 'EUTRA CI', + 4 : 'NR CI', 129 : 'HESSID', 130 : 'SSID', 132 : 'BSSID' @@ -610,7 +785,7 @@ class _LocFieldWLAN(Envelope): Uint8('Type', dic=_LocFieldType_dict), Alt('Cont', GEN={ 129 : Buf('HESSID', bl=48, rep=REPR_HEX), - 130 : Buf('SSID', rep=REPR_HEX), + 130 : Buf('SSID', rep=REPR_HEX), # SSID should be no more than 32 bytes 132 : Buf('BSSID', bl=48, rep=REPR_HEX), }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), @@ -621,45 +796,15 @@ class _LocFieldWLAN(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) self[0].set_valauto(lambda: 1 + self[2].get_len()) - self[2].set_blauto(lambda: (self[0].get_val()-1)<<3) - - -# Figure 5.3.2.11b: Location field {entry type= 3GPP location} -class _LocField3GPP(Envelope): - _GEN = ( - Uint8('Len'), - Uint8('Type', dic=_LocFieldType_dict), - Alt('Cont', GEN={ - #1 : Buf('TAC'), # WNG: a TAC is not 8 bits (but 16 in 4G and 24 in 5G) ! - #2 : Uint16('EUTRACellID'), # WNG: a LTE CellID is not 16 bits (but 28) ! - #4 : Uint24('NRCellID'), # WNG: a NR CellID is not 24 bits (but 36) ! - }, - DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), - sel=lambda self: self.get_env()['Type'].get_val() - ) - ) - - def __init__(self, *args, **kwargs): - Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 1 + self[2].get_len()) - self[2].set_blauto(lambda: (self[0].get_val()-1)<<3) - - -# Figure 5.3.2.11a: Location field {entry type= Geo location} -class _LocFieldGeo(Envelope): - _GEN = ( - Uint32('Lat'), - Uint32('Long'), - Uint16('Radius') - ) + self[2].set_blauto(lambda: (self[0].get_val() - 1)<<3) # Figure 5.3.2.10a: Location sub entry {entry type= WLAN location or Geo location} -class _LocSubentryGeo(Envelope): +class _LocSubentryWLAN(Envelope): _GEN = ( Uint16('Len'), Uint8('Num'), - Sequence('Fields', GEN=_LocFieldGeo('LocGeo')) + Sequence('Fields', GEN=_LocFieldWLAN('LocWLAN')) ) def __init__(self, *args, **kwargs): @@ -667,21 +812,38 @@ def __init__(self, *args, **kwargs): self[0].set_valauto(lambda: 1 + self[2].get_len()) self[1].set_valauto(lambda: self[2].get_num()) self[2].set_numauto(lambda: self[1].get_val()) + self[2].set_blauto(lambda: (self[0].get_val() - 1)<<3) -# Figure 5.3.2.10a: Location sub entry {entry type= WLAN location or Geo location} -class _LocSubentryWLAN(Envelope): +# Figure 5.3.2.11b: Location field {entry type= 3GPP location} +class _LocField3GPP(Envelope): _GEN = ( - Uint16('Len'), - Uint8('Num'), - Sequence('Fields', GEN=_LocFieldWLAN('LocWLAN')) + Uint8('Len'), + Uint8('Type', dic=_LocFieldType_dict), + Alt('Cont', GEN={ + 1 : Uint24('TAC', rep=REPR_HEX), + 2 : Envelope('EUTRA_CI', GEN=( + Uint('spare', bl=4, rep=REPR_HEX), + Uint('Value', bl=28, rep=REPR_HEX), + )), + 4 : Envelope('NR_CI', GEN=( + Uint('spare', bl=4, rep=REPR_HEX), + Uint('Value', bl=36, rep=REPR_HEX), + )), + # Not sure those should be there for Loc3GPP + 129 : Buf('ESSID', bl=48, rep=REPR_HEX), + 130 : Buf('SSID'), # SSID should be no more than 32 bytes + 132 : Buf('BSSID', bl=48, rep=REPR_HEX), + }, + DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), + sel=lambda self: self.get_env()['Type'].get_val() + ) ) def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) self[0].set_valauto(lambda: 1 + self[2].get_len()) - self[1].set_valauto(lambda: self[2].get_num()) - self[2].set_numauto(lambda: self[1].get_val()) + self[2].set_blauto(lambda: (self[0].get_val() - 1)<<3) # Figure 5.3.2.10: Location sub entry {entry type= 3GPP location} @@ -698,6 +860,7 @@ def __init__(self, *args, **kwargs): self[0].set_valauto(lambda: 4 + self[3].get_len()) self[2].set_valauto(lambda: self[3].get_num()) self[3].set_numauto(lambda: self[2].get_val()) + self[3].set_blauto(lambda: (self[0].get_val() - 4)<<3) # Figure 5.3.2.6: Location entry @@ -707,7 +870,6 @@ class LocationEntry(Envelope): Uint('Type', bl=2, dic={1:'3GPP', 2:'WLAN', 3:'Geo'}), Uint('Num', bl=6), Sequence('SubEntries', GEN=Alt('LocationSubEntry', GEN={ - # TODO: verify Type values 1 : _LocSubentry3GPP('SubEntry3GPP'), 2 : _LocSubentryWLAN('SubEntryWLAN'), 3 : _LocSubentryGeo('SubEntryGeo') @@ -721,7 +883,7 @@ def __init__(self, *args, **kwargs): self[0].set_valauto(lambda: 1 + self[3].get_len()) self[2].set_valauto(lambda: self[3].get_num()) self[3].set_numauto(lambda: self[2].get_val()) - self[3].set_blauto(lambda: (self[0].get_val()-1)<<3) + self[3].set_blauto(lambda: (self[0].get_val() - 1)<<3) # Figure 5.3.2.5: Validity area @@ -737,12 +899,11 @@ def __init__(self, *args, **kwargs): self[0].set_valauto(lambda: 1 + self[2].get_len()) self[1].set_valauto(lambda: self[2].get_num()) self[2].set_numauto(lambda: self[1].get_val()) - - + self[2].set_blauto(lambda: (self[0].get_val() - 1)<<3) # Figure 5.3.2.20: ToD sub field {field type = "day of the week"} -class _TimeOfDayDay(Envelope): +class _TimeOfDayDoW(Envelope): _GEN = ( Uint('spare', val=1, bl=1), Uint('Mon', bl=1), @@ -769,11 +930,12 @@ class _TimeOfDayField(Envelope): Uint8('Len'), Uint8('Type', dic=_TimeOfDayFieldType_dict), Alt('Cont', GEN={ + # Next 4 are date strings, as defined in ISO 8601:2004 1 : Buf('TimeStart'), 2 : Buf('TimeStop'), 4 : Buf('DateStart'), 8 : Buf('DateStop'), - 16 : _TimeOfDayDay('DayOfWeek') + 16 : _TimeOfDayDoW('DayOfWeek') }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), sel=lambda self: self.get_env().get_env()['Type'].get_val()) @@ -793,6 +955,7 @@ def __init__(self, *args, **kwargs): self[0].set_valauto(lambda: 1 + self[2].get_len()) self[1].set_valauto(lambda: self[2].get_num()) self[2].set_numauto(lambda: self[1].get_val()) + self[2].set_blauto(lambda: (self[0].get_val() - 1)<<3) # Figure 5.3.2.15: Time of day @@ -808,6 +971,7 @@ def __init__(self, *args, **kwargs): self[0].set_valauto(lambda: 1 + self[2].get_len()) self[1].set_valauto(lambda: self[2].get_num()) self[2].set_numauto(lambda: self[1].get_val()) + self[2].set_blauto(lambda: (self[0].get_val() - 1)<<3) # Figure 5.3.2.3 @@ -816,13 +980,15 @@ class WLANSPRule(Envelope): Uint16('Len'), Uint8('Id'), Uint8('Priority'), - Uint('Roaming', bl=1, dic={0:'rule only valid when UE is not roaming', 1:'rule only valid when UE is roaming'}), + Uint('Roaming', bl=1, dic={ + 0 : 'rule only valid when UE is not roaming', + 1 : 'rule only valid when UE is roaming'}), Uint('ValidityAreaInd', bl=1), Uint('3GPPLocInd', bl=1), Uint('WLANLocInd', bl=1), Uint('GeoLocInd', bl=1), Uint('TimeOfDayInd', bl=1), - Uint('spare', bl=2), + Uint('spare', bl=2, rep=REPR_HEX), _WLANSelectionCriteria('SelectionCriteria'), _WLANValidityArea('ValidityArea'), # optional _WLANTimeOfDay('TimeOfDay') # optional @@ -830,11 +996,14 @@ class WLANSPRule(Envelope): def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: 3 + self['SelectionCriteria'].get_len() + \ - self['ValidityArea'].get_len() + self['TimeOfDay'].get_len()) - #self['ValidityArea'].set_transauto(lambda: self['ValidityAreaInd'].get_val() == 0) - self['ValidityArea'].set_transauto(lambda: self['GeoLocInd'].get_val() == 0) - self['TimeOfDay'].set_transauto(lambda: self['TimeOfDayInd'].get_val() == 0) + self['Len'].set_valauto(lambda: + 3 + self['SelectionCriteria'].get_len() + self['ValidityArea'].get_len() + self['TimeOfDay'].get_len() + ) + + # The 2 indicators for ValidityArea and TimeOfDay are used to hint about existing content + # in the ValidityArea and TimeOfDay + # The 3 indicators for 3GPPLoc, WLANLoc and GeoLoc are used to hint about what exists + # in the ValidityArea # Figure 5.3.3.2.2: N3AN node selection information entry @@ -845,94 +1014,156 @@ def __init__(self, *args, **kwargs): class _N3ANNodeSelEntry(Envelope): _GEN = ( - Uint8('Len', val=4), + Uint8('Len'), PLMN(), - Uint('FQDNFormat', bl=2, dic=_N3ANNodeSelFQDNFmt_dict), + Uint('FQDNFmt', bl=2, dic=_N3ANNodeSelFQDNFmt_dict), Uint('Preference', bl=1, dic={0:'N3IWF is preferred', 1:'ePDG is preferred'}), - Uint('Priority', bl=5) + Uint('Priority', bl=5), + Buf('ext', val=b'', rep=REPR_HEX) ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Len'].set_valauto(lambda: 4 + self['ext'].get_len()) + self['ext'].set_blauto(lambda: (self['Len'].get_val() - 4)<<3) -# Figure 5.3.3.2.1: Content of N3AN node selection information -class _N3ANNodeSelInfo(Envelope): +# Figure 5.3.3.3.2: Home N3IWF identifier entry (Home N3IWF identifier type = "IPv4", "IPv6" or "IPv4v6") +# Figure 5.3.3.3.3: Home N3IWF identifier entry (Home N3IWF identifier type = "FQDN") +# Figure 5.3.3.3.4: Home N3IWF identifier entry (Home N3IWF identifier type = "SNPN FQDN") +class _HomeN3IWFIdentEntrySNPNFQDN(Envelope): _GEN = ( - Uint16('Len'), - Sequence('Entries', GEN=_N3ANNodeSelEntry('N3ANNodeSelEntry')) + Uint8('Len'), + Uint('MCCDigit2', bl=4), + Uint('MCCDigit1', bl=4), + Uint('spare', bl=4, rep=REPR_HEX), + Uint('MCCDigit3', bl=4), + FQDN() ) def __init__(self, *args, **kwargs): Envelope.__init__(self, *args, **kwargs) - self[0].set_valauto(lambda: self[1].get_len()) - self[1].set_blauto(lambda: self[0].get_val()<<3) + self['Len'].set_valauto(lambda: 2 + self['FQDN'].get_len()) + self['FQDN'].set_blauto(lambda: (self['Len'].get_val() - 2)<<3) -# Table 5.3.3.3.1: Home N3IWF identifier entry (type = IP address type) -# Table 5.3.3.3.2: Home N3IWF identifier entry (type = FQDN) class _HomeN3IWFIdentEntry(Envelope): _GEN = ( - Uint8('Type', val=1, dic={1:'IPv4', 2:'IPv6', 3:'IPv4v6', 4:'FQDN'}), + Uint8('Type', val=1, dic={1:'IPv4', 2:'IPv6', 3:'IPv4v6', 4:'FQDN', 5:'SNPN FQDN'}), Alt('', GEN={ - 1 : Buf('IPv4', bl=32, rep=REPR_HEX), - 2 : Buf('IPv6', bl=128, rep=REPR_HEX), + 1 : IPAddr('IPv4', bl=32, rep=REPR_HEX), + 2 : IPAddr('IPv6', bl=128, rep=REPR_HEX), 3 : Envelope('IPv4v6', GEN=( - Buf('IPv4', bl=32, rep=REPR_HEX), - Buf('IPv6', bl=128, rep=REPR_HEX)) + IPAddr('IPv4', bl=32, rep=REPR_HEX), + IPAddr('IPv6', bl=128, rep=REPR_HEX)) ), - 4 : _TrafficDescCompFQDN('FQDN') + 4 : _TrafficDescCompFQDN('FQDN'), + 5 : _HomeN3IWFIdentEntrySNPNFQDN('SNPNFQDN'), }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), sel=lambda self: self.get_env()['Type'].get_val()) ) -# Figure 5.3.3.3.1: Content of home N3IWF identifier configuration -class _HomeN3IWFIdentConfig(Envelope): +# Figure 5.3.3.4.2: Home ePDG identifier entry (Home ePDG identifier type = "IPv4", "IPv6" or "IPv4v6") +# Figure 5.3.3.4.3: Home ePDG identifier entry (Home ePDG identifier type = "FQDN") +class _HomeEPDGIdentEntry(Envelope): _GEN = ( - Uint8('Type', val=1), - Uint16('Len'), - Sequence('Entries', GEN=_HomeN3IWFIdentEntry('HomeN3IWFIdentEntry')) + Uint8('Type', val=1, dic={1:'IPv4', 2:'IPv6', 3:'IPv4v6', 4:'FQDN'}), + Alt('', GEN={ + 1 : IPAddr('IPv4', bl=32, rep=REPR_HEX), + 2 : IPAddr('IPv6', bl=128, rep=REPR_HEX), + 3 : Envelope('IPv4v6', GEN=( + IPAddr('IPv4', bl=32, rep=REPR_HEX), + IPAddr('IPv6', bl=128, rep=REPR_HEX)) + ), + 4 : _TrafficDescCompFQDN('FQDN'), + }, + DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), + sel=lambda self: self.get_env()['Type'].get_val()) ) - - def __init__(self, *args, **kwargs): - Envelope.__init__(self, *args, **kwargs) - self[1].set_valauto(lambda: self[2].get_len()) - self[2].set_blauto(lambda: self[1].get_val()<<3) -# Figure 5.3.3.4.1: Content of home ePDG identifier configuration -class _HomeEPDGIdentConfig(Envelope): +# Figure 5.3.3.5.2: Extended home N3IWF identifier entry +class _ExtHomeN3IWFIdentEntry(Envelope): _GEN = ( - Uint8('Type', val=2), - Uint16('Len'), - Sequence('Entries', GEN=_HomeN3IWFIdentEntry('HomeEPDGIdentEntry')) - ) + _HomeN3IWFIdentEntry('HomeN3IWFEntry'), + NSSAI() + ) -# Figure 5.3.3.1.1: ANDSP info containing N3AN node configuration information -class N3AN(Envelope): +# Figure 5.3.3.6.2: slice-specific N3IWF prefix entry +class _SSN3IWFPrefixConfig(Envelope): _GEN = ( - _N3ANNodeSelInfo('N3ANNodeSelInfo'), - _HomeN3IWFIdentConfig('HomeN3IWFIdentConfig'), - _HomeEPDGIdentConfig('HomeEPDGIdentConfig') + FQDN('Prefix'), # should be a single label + NSSAI() ) -# Figure 5.3.2.1 +# Table 5.3.3.1.2: Content of N3AN node configuration informatio +_N3ANNodeConfigInfoType_dict = { + 1 : 'Home N3IWF identifier', + 2 : 'Home ePDG identifier', + 3 : 'Extended home N3IWF identifier', + 4 : 'Slice-specific N3IWF prefix' + } + +# Figure 5.3.3.1.2: N3AN node configuration information contents +class N3ANNodeConfig(Envelope): + _GEN = ( + Uint16('LenSelInfo'), + Sequence('SelInfo', GEN=_N3ANNodeSelEntry('SelEntry')), + # + Uint8('TypeHomeN3IWF', val=1, dic=_N3ANNodeConfigInfoType_dict), + Uint16('LenHomeN3IWFI'), + Sequence('HomeN3IWF', GEN=_HomeN3IWFIdentEntry('HomeN3IWFEntry')), + # + Uint8('TypeHomeEPDG', val=2, dic=_N3ANNodeConfigInfoType_dict), + Uint16('LenHomeEPDG'), + Sequence('HomeEPDG', GEN=_HomeEPDGIdentEntry('HomeEPDGEntry')), + # + Uint8('TypeExtHomeN3IWF', val=3, dic=_N3ANNodeConfigInfoType_dict), + Uint16('LenExtHomeN3IWFI'), + Sequence('ExtHomeN3IWF', GEN=_ExtHomeN3IWFIdentEntry('ExtHomeN3IWFEntry')), + # + Uint8('TypeSSN3IWFPref', val=4, dic=_N3ANNodeConfigInfoType_dict), + Uint16('LenSSN3IWFPref'), + Sequence('SSN3IWFPref', GEN=_SSN3IWFPrefixConfig('SSN3IWFPrefEntry')), + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['LenSelInfo'].set_valauto(lambda: self['SelInfo'].get_len()) + self['SelInfo'].set_blauto(lambda: self['LenSelInfo'].get_val()<<3) + self['LenHomeN3IWFI'].set_valauto(lambda: self['HomeN3IWF'].get_len()) + self['HomeN3IWF'].set_blauto(lambda: self['LenHomeN3IWFI'].get_val()<<3) + self['LenHomeEPDG'].set_valauto(lambda: self['HomeEPDG'].get_len()) + self['HomeEPDG'].set_blauto(lambda: self['LenHomeEPDG'].get_val()<<3) + self['LenExtHomeN3IWFI'].set_valauto(lambda: self['ExtHomeN3IWF'].get_len()) + self['ExtHomeN3IWF'].set_blauto(lambda: self['LenExtHomeN3IWFI'].get_val()<<3) + self['LenSSN3IWFPref'].set_valauto(lambda: self['SSN3IWFPref'].get_len()) + self['SSN3IWFPref'].set_blauto(lambda: self['LenSSN3IWFPref'].get_val()<<3) + + +# Table 5.3.1.1: ANDSP information format _ANDSPInfoType_dict = { 0 : 'Reserved', 1 : 'WLANSP', 2 : 'N3AN node configuration information' } + # Figure 5.3.1.3: ANDSP Info +# Figure 5.3.2.1: ANDSP Info type = {WLANSP} +# Figure 5.3.3.1.1: ANDSP info containing N3AN node configuration information, where x=k class ANDSPInfo(Envelope): _GEN = ( Uint('spare', bl=4, rep=REPR_HEX), - Uint('Type', bl=4, dic=_ANDSPInfoType_dict), + Uint('Type', val=1, bl=4, dic=_ANDSPInfoType_dict), Uint16('Len'), Alt('Cont', GEN={ 1: Sequence('WLANSPRules', GEN=WLANSPRule()), - 2: N3AN() + 2: N3ANNodeConfig() }, DEFAULT=Buf('unk', val=b'', rep=REPR_HEX), sel=lambda self: self.get_env()['Type'].get_val()) @@ -944,7 +1175,16 @@ def __init__(self, *args, **kwargs): self[3].set_blauto(lambda: self[2].get_val()<<3) -# Figure 5.3.1.2: ANDSP contents -class ANDSPInfos(Sequence): - _GEN = ANDSPInfo() - +# Figure 5.3.1.1: UE policy part when UE policy part type = {ANDSP} +class ANDSPInfos(Envelope): + _GEN = ( + Uint16('Len'), + Uint('spare', bl=4, rep=REPR_HEX), + Uint('Type', val=2, bl=4, dic={2:'ANDSP'}), + Sequence('Cont', GEN=ANDSPInfo()) + ) + + def __init__(self, *args, **kwargs): + Envelope.__init__(self, *args, **kwargs) + self['Len'].set_valauto(lambda: 1 + self['Cont'].get_len()) + self['Cont'].set_blauto(lambda: (self['Len'].get_val() - 1)<<3) From d6972d7f4a182e18f768ce0347159f80c5947c54 Mon Sep 17 00:00:00 2001 From: mitshell Date: Tue, 13 Oct 2026 21:43:33 +0200 Subject: [PATCH 2/2] test: add NAS5G with URSP policies --- test/test_mobile.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/test_mobile.py b/test/test_mobile.py index 9af255e..1f38dc5 100644 --- a/test/test_mobile.py +++ b/test/test_mobile.py @@ -164,7 +164,11 @@ '2e0501c211000901000631310101ff0506060001060001290501ac115f012506056461746131', # 5GSM PDU Sess Estab Accept '7e00670100072e0602c1000091120681220401000001250706766973696f6e', # 5GMM UL Trans, 5GSM PDU Sess Estab Req '7e0100000000067e006801002d2e0602c2110009ff000631310101ff050603f42403f4242905010b000033220401000001250706766973696f6e1206', # 5GMM DL Trans, 5GSM PDU Sess Estab Accept - '7e00670500020002', # UE policy complete + '7e006805001e9701001a001800f11000130002000f01000c010001010006000401000184', # Manage UE policy cmd - ProSe multipath + '7e006805002797010023002100f110001c00020018010015010009a20770696e2d30303100070005ff00020101', # Manage UE policy cmd - PID ID + '7e006805002897010024002200f110001d0002001901001601000aa30867726f75702d303100070005ff00020101', # Manage UE policy cmd - Con Grp ID + '7e00680500530101004f004d00f11000480003004401001f01000b880908627573696e657373000f000dff000a020401e2e11108011001002002000101001a0018ff0015020401def222040908696e7465726e657408011001', # Manage UE policy cmd + '7e00670500020102', # Manage UE policy cmpl )))