1971 lines
69 KiB
Python
1971 lines
69 KiB
Python
# -*- coding: UTF-8 -*-
|
|
#/**
|
|
# * Software Name : pycrate
|
|
# * Version : 0.4
|
|
# *
|
|
# * Copyright 2017. Benoit Michau. ANSSI.
|
|
# *
|
|
# * This library is free software; you can redistribute it and/or
|
|
# * modify it under the terms of the GNU Lesser General Public
|
|
# * License as published by the Free Software Foundation; either
|
|
# * version 2.1 of the License, or (at your option) any later version.
|
|
# *
|
|
# * This library is distributed in the hope that it will be useful,
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# * Lesser General Public License for more details.
|
|
# *
|
|
# * You should have received a copy of the GNU Lesser General Public
|
|
# * License along with this library; if not, write to the Free Software
|
|
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# * MA 02110-1301 USA
|
|
# *
|
|
# *--------------------------------------------------------
|
|
# * File Name : pycrate_corenet/ProcCNEMM.py
|
|
# * Created : 2017-12-05
|
|
# * Authors : Benoit Michau
|
|
# *--------------------------------------------------------
|
|
#*/
|
|
|
|
__all__ = [
|
|
'EMMSigProc',
|
|
'EMMGUTIReallocation',
|
|
'EMMAuthentication',
|
|
'EMMSecurityModeControl',
|
|
'EMMIdentification',
|
|
'EMMInformation',
|
|
'EMMAttach',
|
|
'EMMDetachUE',
|
|
'EMMDetachCN',
|
|
'EMMTrackingAreaUpdate',
|
|
'EMMServiceRequest',
|
|
'EMMExtServiceRequest',
|
|
'EMMCPServiceRequest',
|
|
'EMMDLNASTransport',
|
|
'EMMULNASTransport',
|
|
'EMMDLGenericNASTransport',
|
|
'EMMULGenericNASTransport',
|
|
#
|
|
'EMMProcUeDispatcher',
|
|
'EMMProcUeDispatcherStr',
|
|
'EMMProcCnDispatcher',
|
|
'EMMProcCnDispatcherStr'
|
|
]
|
|
|
|
from .utils import *
|
|
from .ProcProto import *
|
|
from .ProcCNS1ap import *
|
|
|
|
|
|
TESTING = False
|
|
|
|
#------------------------------------------------------------------------------#
|
|
# NAS EPS Mobility Management signalling procedure
|
|
# TS 24.301, version da0
|
|
# Core Network side
|
|
#------------------------------------------------------------------------------#
|
|
|
|
class EMMSigProc(NASSigProc):
|
|
"""EPS Mobility Management signalling procedure handler
|
|
|
|
instance attributes:
|
|
- Name : procedure name
|
|
- EMM : reference to the UEEMMd instance running this procedure
|
|
- S1 : reference to the UES1d instance connecting the UE
|
|
- Cont : 2-tuple of CN-initiated NAS message(s) and UE-initiated NAS
|
|
message(s)
|
|
- Timer: timer in sec. for this procedure
|
|
- Encod: custom NAS message encoders with fixed values
|
|
- Decod: custom NAS message decoders with transform functions
|
|
"""
|
|
|
|
# tacking all exchanged NAS message within the procedure
|
|
TRACK_PDU = True
|
|
|
|
# potential timer
|
|
Timer = None
|
|
TimerDefault = 4
|
|
|
|
# network initiator message id
|
|
Init = None
|
|
|
|
if TESTING:
|
|
def __init__(self, encod=None):
|
|
self._prepare(encod)
|
|
self._log('DBG', 'instantiating procedure')
|
|
|
|
def _log(self, logtype, msg):
|
|
log('[TESTING] [%s] [EMMSigProc] [%s] %s' % (logtype, self.Name, msg))
|
|
|
|
else:
|
|
def __init__(self, emmd, encod=None, emm_preempt=False, sec=True):
|
|
self._prepare(encod)
|
|
self.EMM = emmd
|
|
self.S1 = emmd.S1
|
|
self.UE = emmd.UE
|
|
self._emm_preempt = emm_preempt
|
|
if emm_preempt:
|
|
self.EMM.ready.clear()
|
|
self._sec = sec
|
|
self._log('DBG', 'instantiating procedure')
|
|
|
|
def _log(self, logtype, msg):
|
|
self.EMM._log(logtype, '[%s] %s' % (self.Name, msg))
|
|
|
|
def output(self):
|
|
self._log('ERR', 'output() not implemented')
|
|
return []
|
|
|
|
def process(self, pdu):
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
#
|
|
self._log('ERR', 'process() not implemented')
|
|
return []
|
|
|
|
def postprocess(self, Proc=None):
|
|
self._log('ERR', 'postprocess() not implemented')
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
def abort(self):
|
|
# abort this procedure, and all procedures started within this one
|
|
ind = self.EMM.Proc.index(self)
|
|
if ind >= 0:
|
|
for p in self.EMM.Proc[ind+1:]:
|
|
p.abort()
|
|
del self.EMM.Proc[ind:]
|
|
if self._emm_preempt:
|
|
# release the EMM stack
|
|
self.EMM.ready.set()
|
|
self._log('INF', 'aborting')
|
|
|
|
def rm_from_emm_stack(self):
|
|
# remove the procedure from the EMM stack of procedures
|
|
try:
|
|
if self.EMM.Proc[-1] == self:
|
|
del self.EMM.Proc[-1]
|
|
except Exception:
|
|
self._log('WNG', 'EMM stack corrupted')
|
|
else:
|
|
if self._emm_preempt:
|
|
# release the EMM stack
|
|
self.EMM.ready.set()
|
|
|
|
def init_timer(self):
|
|
if self.Timer is not None:
|
|
self.TimerValue = getattr(self.EMM, self.Timer, self.TimerDefault)
|
|
self.TimerStart = time()
|
|
self.TimerStop = self.TimerStart + self.TimerValue
|
|
|
|
def get_timer(self):
|
|
if self.Timer is None:
|
|
return None
|
|
else:
|
|
return getattr(self.EMM, self.Timer)
|
|
|
|
def emm_preempt(self):
|
|
self._emm_preempt = True
|
|
self.EMM.ready.clear()
|
|
|
|
#--------------------------------------------------------------------------#
|
|
# common helpers
|
|
#--------------------------------------------------------------------------#
|
|
|
|
def _collect_cap(self):
|
|
if not hasattr(self, 'Cap') or not hasattr(self, 'UEInfo'):
|
|
return
|
|
setseccap = False
|
|
for Cap in self.Cap:
|
|
if Cap in self.UEInfo:
|
|
self.UE.Cap[Cap] = self.UEInfo[Cap]
|
|
if Cap == 'UENetCap':
|
|
setseccap = True
|
|
if setseccap:
|
|
self._log('DBG', 'setting UE security capabilities')
|
|
self.EMM.set_sec_cap()
|
|
|
|
def _chk_imsi(self):
|
|
# arriving here means the UE's IMSI was unknown at first
|
|
Server, imsi = self.UE.Server, self.UE.IMSI
|
|
if not Server.is_imsi_allowed(imsi):
|
|
self.errcause = self.EMM.IDENT_IMSI_NOT_ALLOWED
|
|
return False
|
|
else:
|
|
# update the MTMSI table
|
|
Server.MTMSI[self.UE.MTMSI] = imsi
|
|
#
|
|
if imsi in Server.UE:
|
|
if self.UE != self.UE.Server.UE[imsi]:
|
|
ue = Server.UE[imsi]
|
|
if not ue.merge_eps_handler(self.S1):
|
|
# unable to merge to the existing profile
|
|
self._log('WNG', 'profile for IMSI %s already exists, '\
|
|
'need to reject for reconnection' % imsi)
|
|
# reject so that it will reconnect
|
|
# and get the already existing profile
|
|
self.errcause = self.EMM.ATT_IMSI_PROV_REJECT
|
|
return False
|
|
else:
|
|
return True
|
|
else:
|
|
return True
|
|
else:
|
|
Server.UE[imsi] = self.UE
|
|
# update the Server UE's tables
|
|
if imsi in Server.ConfigUE:
|
|
# update UE's config with it's dedicated config
|
|
self.UE.set_config( Server.ConfigUE[imsi] )
|
|
elif '*' in Server.ConfigUE:
|
|
self.UE.set_config( Server.ConfigUE['*'] )
|
|
return True
|
|
|
|
def _ret_req_imsi(self):
|
|
NasProc = self.EMM.init_proc(EMMIdentification)
|
|
NasProc.set_msg(7, 85, IDType=NAS.IDTYPE_IMSI)
|
|
return NasProc.output()
|
|
|
|
def _ret_auth(self):
|
|
NasProc = self.EMM.init_proc(EMMAuthentication)
|
|
return NasProc.output()
|
|
|
|
def _ret_smc(self, ksi=None, emerg=False):
|
|
NasProc = self.EMM.init_proc(EMMSecurityModeControl)
|
|
if ksi:
|
|
ksi = (ksi[0]<<3) + ksi[1]
|
|
NasProc._set_ksi(ksi, emerg)
|
|
return NasProc.output()
|
|
|
|
def _act_bear(self):
|
|
# reactivate all PDN connections
|
|
erablist, ebilist, brdl, brul = [], [], 0, 0
|
|
for ebi, pdncfg in self.S1.ESM.PDN.items():
|
|
if 'RAB' in pdncfg:
|
|
rabcfg = pdncfg['RAB']
|
|
ebilist.append(ebi)
|
|
# erab ext IE can be Correlation-ID and/or BearerType
|
|
erablist.append({
|
|
'id': 52,
|
|
'criticality': 'reject',
|
|
'value': ('E-RABToBeSetupItemCtxtSUReq', {
|
|
'e-RAB-ID': ebi,
|
|
'e-RABlevelQoSParameters': rabcfg['E-RABlevelQoSParameters'],
|
|
'transportLayerAddress': (bytes_to_uint(inet_aton(rabcfg['SGW-TLA']), 32), 32),
|
|
'gTP-TEID': uint_to_bytes(rabcfg['SGW-GTP-TEID'], 32),
|
|
#'iE-Extensions': [],
|
|
})
|
|
})
|
|
brdl += rabcfg['BitrateDL']
|
|
brul += rabcfg['BitrateUL']
|
|
if not erablist:
|
|
# no PDN connection to reactivate
|
|
return None
|
|
#
|
|
# get the current sec context to setup the eNB security layer
|
|
secctx = self.S1.get_sec_ctx()
|
|
if secctx and 'UESecCap' in self.UE.Cap:
|
|
# create the KeNB
|
|
self._log('DBG', 'NAS UL count for Kenb derivation, %i' % secctx['UL_enb'])
|
|
Kenb, UESecCap = conv_401_A3(secctx['Kasme'], secctx['UL_enb']), self.UE.Cap['UESecCap'][1]
|
|
secctx['Kenb'] = Kenb
|
|
secctx['NCC'] = 0
|
|
secctx['NH'] = conv_401_A4(secctx['Kasme'], Kenb)
|
|
else:
|
|
self._log('WNG', 'no active NAS security context, using the null AS security context')
|
|
Kenb, UESecCap = self.S1.SECAS_NULL_CTX
|
|
#
|
|
IEs = {
|
|
'E_RABToBeSetupListCtxtSUReq': erablist,
|
|
'UEAggregateMaximumBitrate': {
|
|
'uEaggregateMaximumBitRateDL': brdl,
|
|
'uEaggregateMaximumBitRateUL': brul
|
|
},
|
|
'SecurityKey': (bytes_to_uint(Kenb, 256), 256),
|
|
'UESecurityCapabilities': {
|
|
'encryptionAlgorithms': ((UESecCap[1].get_val()<<15) + \
|
|
(UESecCap[2].get_val()<<14) + \
|
|
(UESecCap[3].get_val()<<13), 16),
|
|
'integrityProtectionAlgorithms': ((UESecCap[9].get_val()<<15) + \
|
|
(UESecCap[10].get_val()<<14) + \
|
|
(UESecCap[11].get_val()<<13), 16)
|
|
}
|
|
}
|
|
#
|
|
if self.S1.ICS_RADCAP_INCL and 'UERadioCap' in self.UE.Cap:
|
|
IEs['UERadioCapability'] = self.UE.Cap['UERadioCap'][0]
|
|
if self.S1.ICS_GUMMEI_INCL:
|
|
IEs['GUMMEI'] = gummei_to_asn(self.UE.Server.PLMN,
|
|
self.UE.Server.MME_GID,
|
|
self.UE.Server.MME_CODE)
|
|
if self.S1.ICS_TRACE_ACT:
|
|
IEs['TraceActivation'] = self.S1.ICS_TRACE_ACT
|
|
#
|
|
S1apProc = self.S1.init_s1ap_proc(S1APInitialContextSetup, **IEs)
|
|
if S1apProc:
|
|
# pass the info required for setting the GTPU tunnel
|
|
S1apProc._gtp_add_mobile_ebi = ebilist
|
|
return S1apProc
|
|
else:
|
|
return None
|
|
|
|
|
|
#------------------------------------------------------------------------------#
|
|
# EMM common procedures: TS 24.301, section 5.4
|
|
#------------------------------------------------------------------------------#
|
|
|
|
class EMMGUTIReallocation(EMMSigProc):
|
|
"""GUTI reallocation procedure: TS 24.301, section 5.4.1
|
|
|
|
CN-initiated
|
|
|
|
CN message:
|
|
EMMGUTIReallocCommand (PD 7, Type 80), IEs:
|
|
- Type4LV : GUTI
|
|
- Type4TLV : TAIList
|
|
|
|
UE message:
|
|
EMMGUTIReallocComplete (PD 7, Type 81), IEs:
|
|
None
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMGUTIReallocCommand, ),
|
|
(TS24301_EMM.EMMGUTIReallocComplete, )
|
|
)
|
|
|
|
Init = (7, 80)
|
|
Timer = 'T3450'
|
|
|
|
def output(self, embedded=False):
|
|
# embedded=True is used to embed this procedure within an Attach or TAU
|
|
# hence the output message is not built, only the .mtmsi is available
|
|
# but the procedure still runs and waits for the UE response
|
|
# after all
|
|
# Warning, when the GUTI IE is set by hand, it is not taken into account
|
|
# by EMM procedures
|
|
if 'GUTI' not in self.Encod[self.Init]:
|
|
self.mtmsi = self.UE.get_new_tmsi()
|
|
self.guti = (self.UE.Server.PLMN, self.UE.Server.MME_GID, self.UE.Server.MME_CODE, self.mtmsi)
|
|
self.set_msg(7, 80, GUTI={'type': NAS.IDTYPE_GUTI, 'ident': self.guti})
|
|
else:
|
|
self.mtmsi = None
|
|
self.guti = None
|
|
#
|
|
if not embedded:
|
|
self.encode_msg(7, 80)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self.init_timer()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
else:
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
def process(self, pdu):
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
# just take the new mtmsi in use
|
|
if self.mtmsi is not None:
|
|
self.UE.set_mtmsi(self.mtmsi)
|
|
self._log('INF', 'new M-TMSI set, 0x%.8x' % self.mtmsi)
|
|
else:
|
|
self._log('WNG', 'handcrafted GUTI sent, not updating the local M-TMSI')
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
|
|
class EMMAuthentication(EMMSigProc):
|
|
"""Authentication procedure: TS 24.301, section 5.4.2
|
|
|
|
CN-initiated
|
|
|
|
CN messages:
|
|
EMMAuthenticationRequest (PD 7, Type 82), IEs:
|
|
- Type1V : spare
|
|
- Type1V : NAS_KSI
|
|
- Type3V : RAND
|
|
- Type4LV : AUTN
|
|
|
|
EMMAuthenticationReject (PD 7, Type 84), IEs:
|
|
None
|
|
|
|
UE messages:
|
|
EMMAuthenticationResponse (PD 7, Type 83), IEs:
|
|
- Type4LV : RES
|
|
|
|
EMMAuthenticationFailure (PD 7, Type 92), IEs:
|
|
- Type3V : EMMCause
|
|
- Type4TLV : AUTS
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMAuthenticationRequest, TS24301_EMM.EMMAuthenticationReject),
|
|
(TS24301_EMM.EMMAuthenticationResponse, TS24301_EMM.EMMAuthenticationFailure)
|
|
)
|
|
|
|
Decod = {
|
|
(7, 83): {
|
|
'RES' : lambda x: x[1].get_val()
|
|
},
|
|
(7, 92): {
|
|
'AUTS' : lambda x: x[2].get_val()
|
|
}
|
|
}
|
|
|
|
Init = (7, 82)
|
|
Timer = 'T3460'
|
|
|
|
def output(self):
|
|
# get a new KSI (0..6)
|
|
ksi = self.EMM.get_new_ksi()
|
|
#
|
|
EncodReq = self.Encod[self.Init]
|
|
# in case NAS_KSI is handcrafted, just warn (will certainly fail)
|
|
if 'NAS_KSI' in EncodReq:
|
|
self._log('WNG', 'handcrafted NAS_KSI (%r), generated KSI (%i)'\
|
|
% (EncodReq['NAS_KSI'], self.ksi))
|
|
# in case RAND is handcrafted, we use it for generating the auth vector
|
|
if 'RAND' in EncodReq:
|
|
RAND = EncodReq['RAND']
|
|
else:
|
|
RAND = None
|
|
#
|
|
if not self.UE.USIM or self.EMM.AUTH_2G:
|
|
# WNG: 2G authentication, this is illegal and won't work
|
|
self._log('WNG', 'trying an LTE authentication with a 2G vector')
|
|
self.ctx = 2
|
|
self.ksi = (1, ksi) # mapped ctx
|
|
self.vect = self.UE.Server.AUCd.make_2g_vector(self.UE.IMSI, RAND)
|
|
elif self.EMM.AUTH_3G:
|
|
# WNG: 3g authentication, this is also illegal and should not work
|
|
self.ctx = 3
|
|
self.ksi = (1, ksi) # mapped ctx
|
|
self.vect = self.UE.Server.AUCd.make_3g_vector(self.UE.IMSI, self.EMM.AUTH_AMF, RAND)
|
|
else:
|
|
# 4G authentication
|
|
self.ctx = 4
|
|
self.ksi = (0, ksi) # native ctx
|
|
if self.EMM.AUTH_PLMN:
|
|
self.snid = plmn_str_to_buf(self.EMM.AUTH_PLMN)
|
|
else:
|
|
self.snid = plmn_str_to_buf(self.UE.Server.PLMN)
|
|
self.vect = self.UE.Server.AUCd.make_4g_vector(self.UE.IMSI, self.snid, self.EMM.AUTH_AMF, RAND)
|
|
#
|
|
if self.vect is None:
|
|
# IMSI is not in the AuC db
|
|
self._log('ERR', 'unable to get an authentication vector from AuC')
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
#
|
|
# prepare IEs
|
|
if self.ctx == 2:
|
|
autn = b''
|
|
else:
|
|
autn = self.vect[2]
|
|
if self.EMM.AUTH_AUTN_EXT:
|
|
autn += self.EMM.AUTH_AUTN_EXT
|
|
#
|
|
self.set_msg(7, 82, NAS_KSI=self.ksi, RAND=self.vect[0], AUTN=autn)
|
|
self.encode_msg(7, 82)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self.init_timer()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
|
|
def process(self, pdu):
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
#
|
|
if pdu._name == 'EMMAuthenticationResponse':
|
|
return self._process_resp()
|
|
else:
|
|
return self._process_fail()
|
|
|
|
def _process_resp(self):
|
|
# check if the whole UE response is corresponding to the expected one
|
|
if self.ctx != 2:
|
|
if self.UEInfo['RES'] != self.vect[1]:
|
|
# incorrect response from the UE: auth reject
|
|
self._log('WNG', '%iG authentication reject, XRES %s, RES %s'\
|
|
% (self.ctx, hexlify(self.vect[1]).decode('ascii'),
|
|
hexlify(self.UEInfo['RES']).decode('ascii')))
|
|
self.encode_msg(7, 84)
|
|
self.success = False
|
|
else:
|
|
self._log('DBG', '%iG authentication accepted' % self.ctx)
|
|
self.success = True
|
|
# set the security context
|
|
self.EMM.set_sec_ctx(self.ksi, self.ctx, self.vect)
|
|
else:
|
|
# 2G auth context
|
|
if self.UEInfo['RES'] != self.vect[1][:4]:
|
|
# incorrect response from the UE: auth reject
|
|
self._log('WNG', '2G authentication reject, XRES %s, RES %s'\
|
|
% (hexlify(self.vect[1][:4]).decode('ascii'),
|
|
hexlify(self.UEInfo['RES']).decode('ascii')))
|
|
self.encode_msg(8, 20)
|
|
self.success = False
|
|
else:
|
|
self._log('WNG', '2G authentication accepted')
|
|
self.success = True
|
|
# set a 2G security context
|
|
self.EMM.set_sec_ctx(self.ksi, 2, self.vect)
|
|
#
|
|
self.rm_from_emm_stack()
|
|
if not self.success:
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
else:
|
|
return []
|
|
|
|
def _process_fail(self):
|
|
self.success = False
|
|
if self.UEInfo['EMMCause'].get_val() == 21 and 'AUTS' in self.UEInfo:
|
|
# synch failure
|
|
ret = self.UE.Server.AUCd.synch_sqn(self.UE.IMSI, self.vect[0], self.UEInfo['AUTS'])
|
|
#
|
|
if ret is None:
|
|
# something did not work
|
|
self._log('ERR', 'unable to resynchronize SQN in AuC')
|
|
self.encode_msg(8, 20)
|
|
self.rm_from_emm_stack()
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
#
|
|
elif ret:
|
|
# USIM did not authenticate correctly
|
|
self._log('WNG', 'USIM authentication failed for resynch')
|
|
self.encode_msg(8, 20)
|
|
self.rm_from_emm_stack()
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
#
|
|
else:
|
|
# resynch OK: restart an auth procedure
|
|
self._log('INF', 'USIM SQN resynchronization done')
|
|
self.rm_from_emm_stack()
|
|
# restart a new auth procedure
|
|
NasProc = self.EMM.init_proc(EMMAuthentication)
|
|
return NasProc.output()
|
|
#
|
|
else:
|
|
# UE refused our auth request...
|
|
self._log('ERR', 'UE rejected AUTN, %s' % self.UEInfo['EMMCause'])
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
|
|
class EMMSecurityModeControl(EMMSigProc):
|
|
"""Security mode control procedure: TS 24.301, section 5.4.3
|
|
|
|
CN-initiated
|
|
|
|
CN message:
|
|
EMMSecurityModeCommand (PD 7, Type 93), IEs:
|
|
- Type3V : NASSecAlgo
|
|
- Type1V : spare
|
|
- Type1V : NAS_KSI
|
|
- Type4LV : UESecCap
|
|
- Type1TV : IMEISVReq
|
|
- Type3TV : NonceUE
|
|
- Type3TV : NonceMME
|
|
|
|
UE messages:
|
|
EMMSecurityModeComplete (PD 7, Type 94), IEs:
|
|
- Type4TLV : IMEISV
|
|
|
|
EMMSecurityModeReject (PD 7, Type 95), IEs:
|
|
- Type3V : EMMCause
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMSecurityModeCommand, ),
|
|
(TS24301_EMM.EMMSecurityModeComplete, TS24301_EMM.EMMSecurityModeReject)
|
|
)
|
|
|
|
Decod = {
|
|
(7, 94): {
|
|
'IMEISV': lambda x: x[2].decode()
|
|
}
|
|
}
|
|
|
|
Init = (7, 94)
|
|
Timer = 'T3460'
|
|
|
|
def _set_ksi(self, ksi, emerg):
|
|
if ksi is None:
|
|
self.ksi = self.EMM.get_any_ksi()
|
|
else:
|
|
self.ksi = ksi
|
|
if not emerg:
|
|
self.EMM.set_sec_ctx_smc(self.ksi)
|
|
self.S1.SEC['KSI'] = self.ksi
|
|
|
|
def output(self):
|
|
if not hasattr(self, 'ksi'):
|
|
if 'NAS_KSI' in self.Encod[self.Init]:
|
|
nasksi = self.Encod[self.Init]['NAS_KSI']
|
|
ksi = (nasksi[0]<<3) + nasksi[1]
|
|
else:
|
|
ksi = None
|
|
self._set_ksi(ksi, emerg=False)
|
|
try:
|
|
self.secctx = self.S1.SEC[self.ksi]
|
|
except KeyError:
|
|
# no security ctxt available at all
|
|
self._log('WNG', 'no security context available, using an emergency one with KSI %i' % self.ksi)
|
|
self.EMM.set_sec_ctx_emerg(self.ksi)
|
|
self.secctx = self.S1.SEC[self.ksi]
|
|
EncodReq = self.Encod[self.Init]
|
|
# in case any of the IEs is handcrafted, we warn (will certainly fail)
|
|
if 'NASSecAlgo' in EncodReq or 'NAS_KSI' in EncodReq or 'UESecCap' in EncodReq:
|
|
self._log('WNG', 'handcrafted IEs: %r' % EncodReq)
|
|
# prepare IEs for the SMC
|
|
IEs = {'NASSecAlgo': {'CiphAlgo': self.secctx['EEA'], 'IntegAlgo': self.secctx['EIA']},
|
|
'NAS_KSI' : (self.ksi>>3, self.ksi&0x7),
|
|
'UESecCap' : self.EMM.get_sec_cap()}
|
|
if self.UE.IMEISV is None and self.EMM.SMC_IMEISV_REQ:
|
|
IEs['IMEISVReq'] = 1
|
|
# TODO: check support of NonceUE / NonceMME for mobility procedures
|
|
#
|
|
self.set_msg(7, 93, **IEs)
|
|
self.encode_msg(7, 93)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self.init_timer()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
|
|
def process(self, pdu):
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
self.rm_from_emm_stack()
|
|
if pdu._name == 'EMMSecurityModeComplete':
|
|
self.success = True
|
|
if 'IMEISV' in self.UEInfo:
|
|
self.UE.set_ident_from_ue(*self.UEInfo['IMEISV'], dom='EPS')
|
|
self._log('INF', 'success, EEA%i / EIA%i selected' % (self.secctx['EEA'], self.secctx['EIA']))
|
|
return []
|
|
else:
|
|
self.success = False
|
|
self._log('WNG', 'failure, %r' % self.UEInfo['EMMCause'])
|
|
return []
|
|
|
|
|
|
class EMMIdentification(EMMSigProc):
|
|
"""Identification procedure: TS 24.301, section 5.4.4
|
|
|
|
CN-initiated
|
|
|
|
CN message:
|
|
EMMIdentityRequest (PD 7, Type 85), IEs:
|
|
- Type1V : spare
|
|
- Type1V : IDType
|
|
|
|
UE message:
|
|
EMMIdentityResponse (PD 7, Type 86), IEs:
|
|
- Type4LV : ID
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMIdentityRequest, ),
|
|
(TS24301_EMM.EMMIdentityResponse, )
|
|
)
|
|
|
|
Decod = {
|
|
(7, 86): {
|
|
'ID': lambda x: x[1].decode(),
|
|
}
|
|
}
|
|
|
|
Init = (7, 85)
|
|
Timer = 'T3470'
|
|
|
|
def output(self):
|
|
# build the Id Request msg, Id type has to be set by the caller
|
|
self.encode_msg(7, 85)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self.init_timer()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
|
|
def process(self, pdu):
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
# get the identity IE value
|
|
self.IDType = self._nas_tx['IDType'][0].get_val()
|
|
#
|
|
if self.UEInfo['ID'][0] != self.IDType :
|
|
self._log('WNG', 'identity responded not corresponding to type requested '\
|
|
'(%i instead of %i)' % (self.UEInfo['ID'][0], self.IDType))
|
|
self._log('INF', 'identity responded, %r' % self._nas_rx['ID'][1])
|
|
self.UE.set_ident_from_ue(*self.UEInfo['ID'], dom='EPS')
|
|
#
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
|
|
class EMMInformation(EMMSigProc):
|
|
"""EMM information procedure: TS24.301, section 5.4.5
|
|
|
|
CN-initiated
|
|
|
|
CN message:
|
|
EMMInformation (PD 7, Type 97), IEs:
|
|
- Type4TLV : NetFullName
|
|
- Type4TLV : NetShortName
|
|
- Type3TV : LocalTimeZone
|
|
- Type3TV : UnivTimeAndTimeZone
|
|
- Type4TLV : DLSavingTime
|
|
|
|
UE message:
|
|
None
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMInformation, ),
|
|
None
|
|
)
|
|
|
|
Init = (7, 97)
|
|
|
|
def output(self):
|
|
self.encode_msg(7, 97)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self._log('INF', '%r' % self.Encod[(7, 97)])
|
|
self.rm_from_emm_stack()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
|
|
|
|
#------------------------------------------------------------------------------#
|
|
# EMM specific procedures: TS 24.301, section 5.5
|
|
#------------------------------------------------------------------------------#
|
|
|
|
class EMMAttach(EMMSigProc):
|
|
"""Attach procedure: TS 24.301, section 5.5.1
|
|
|
|
UE-initiated
|
|
|
|
CN messages:
|
|
EMMAttachAccept (PD 7, Type 66), IEs:
|
|
- Type1V : spare
|
|
- Type1V : EPSAttachResult
|
|
- Type3V : T3412
|
|
- Type4LV : TAIList
|
|
- Type6LVE : ESMContainer
|
|
- Type4TLV : GUTI
|
|
- Type3TV : LAI
|
|
- Type4TLV : ID
|
|
- Type3TV : EMMCause
|
|
- Type3TV : T3402
|
|
- Type3TV : T3423
|
|
- Type4TLV : EquivPLMNList
|
|
- Type4TLV : EmergNumList
|
|
- Type4TLV : EPSNetFeat
|
|
- Type1TV : AddUpdateRes
|
|
- Type4TLV : T3412Ext
|
|
- Type4TLV : T3324
|
|
- Type4TLV : ExtDRXParam
|
|
- Type1TV : SMSServStat
|
|
|
|
EMMAttachReject (PD 7, Type 68), IEs:
|
|
- Type3V : EMMCause
|
|
- Type6TLVE : ESMContainer
|
|
- Type4TLV : T3346
|
|
- Type4TLV : T3402
|
|
- Type1TV : ExtEMMCause
|
|
|
|
UE messages:
|
|
EMMAttachRequest (PD 7, Type 65), IEs:
|
|
- Type1V : NAS_KSI
|
|
- Type1V : EPSAttachType
|
|
- Type4LV : EPSID
|
|
- Type4LV : UENetCap
|
|
- Type6LVE : ESMContainer
|
|
- Type3TV : OldPTMSISign
|
|
- Type4TLV : AddGUTI
|
|
- Type3TV : OldTAI
|
|
- Type3TV : DRXParam
|
|
- Type4TLV : MSNetCap
|
|
- Type3TV : OldLAI
|
|
- Type1TV : TMSIStatus
|
|
- Type4TLV : MSCm2
|
|
- Type4TLV : MSCm3
|
|
- Type4TLV : SuppCodecs
|
|
- Type1TV : AddUpdateType
|
|
- Type4TLV : VoiceDomPref
|
|
- Type1TV : DeviceProp
|
|
- Type1TV : OldGUTIType
|
|
- Type1TV : MSNetFeatSupp
|
|
- Type4TLV : TMSIBasedNRICont
|
|
- Type4TLV : T3324
|
|
- Type4TLV : T3412Ext
|
|
- Type4TLV : ExtDRXParam
|
|
|
|
EMMAttachComplete (PD 7, Type 67), IEs:
|
|
- Type6LVE : ESMContainer
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMAttachAccept, TS24301_EMM.EMMAttachReject),
|
|
(TS24301_EMM.EMMAttachRequest, TS24301_EMM.EMMAttachComplete)
|
|
)
|
|
|
|
Decod = {
|
|
(7, 65): {
|
|
'NAS_KSI' : lambda x: (x[0][0].get_val(), x[0][1].get_val()),
|
|
'EPS_ID' : lambda x: x[1].decode(),
|
|
'OldTAI' : lambda x: x[1].decode(),
|
|
'OldLAI' : lambda x: x[1].decode(),
|
|
},
|
|
}
|
|
|
|
Cap = ('UENetCap', 'DRXParam', 'MSNetCap', 'MSCm2', 'MSCm3', 'SuppCodecs',
|
|
'VoiceDomPref', 'DeviceProp', 'MSNetFeatSupp', 'ExtDRXParam')
|
|
|
|
Timer = 'T3450'
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
self.emm_preempt()
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
#
|
|
if pdu._name == 'EMMAttachRequest':
|
|
# AttachRequest
|
|
self.errcause, self.UEInfo = None, {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
return self._process_req()
|
|
else:
|
|
# AttachComplete
|
|
self.errcause, self.CompInfo = None, {}
|
|
self.decode_msg(pdu, self.CompInfo)
|
|
return self._process_comp()
|
|
|
|
def _process_req(self):
|
|
#
|
|
if self.UEInfo['EPSID'][0] == NAS.IDTYPE_GUTI and self.UE.MTMSI is None:
|
|
self.UE.MTMSI = self.UEInfo['EPSID'][1][3]
|
|
#
|
|
att_type = self.UEInfo['EPSAttachType']
|
|
self.att_type = att_type.get_val()
|
|
#if self.att_type == 2 and 'TMSIStatus' not in self.UEInfo \
|
|
#and 'TMSIBasedNRICont' not in self.UEInfo:
|
|
# # downgrade to EPS-only attachment
|
|
# self.att_type = 1
|
|
self._log('INF', 'request type %i (%s)' % (self.att_type, att_type._dic[self.att_type]))
|
|
# collect capabilities
|
|
self._collect_cap()
|
|
#
|
|
# check for emergency attach
|
|
if self.att_type == 6:
|
|
if self.EMM.ATT_EMERG:
|
|
self.errcause = self.EMM.ATT_EMERG
|
|
return self.output()
|
|
else:
|
|
# jump directly to the smc with EEA0 / EIA0
|
|
self.EMM.set_sec_ctx_emerg()
|
|
# emergency ctx has always ksi 0
|
|
return self._ret_smc((0, 0), emerg=True)
|
|
#
|
|
# check local ID
|
|
elif self.UE.IMSI is None:
|
|
# UEd was created based on a S-TMSI provided at the RRC layer
|
|
# -> we need to get its IMSI before continuing
|
|
try:
|
|
del self.UE.Server._UEpre[self.UE.MTMSI]
|
|
except Exception:
|
|
pass
|
|
#
|
|
if self.UEInfo['EPSID'][0] == 1:
|
|
# IMSI is provided at the NAS layer
|
|
self.UE.set_ident_from_ue(*self.UEInfo['EPSID'], dom='EPS')
|
|
if not self._chk_imsi():
|
|
# IMSI not allowed
|
|
return self.output()
|
|
else:
|
|
# need to request the IMSI, prepare an id request procedure
|
|
return self._ret_req_imsi()
|
|
#
|
|
if self.EMM.require_auth(self, ksi=self.UEInfo['NAS_KSI']):
|
|
return self._ret_auth()
|
|
else:
|
|
# no auth procedure, ksi submitted by the UE is valid
|
|
# set UL NAS count for further KeNB derivation
|
|
try:
|
|
secctx = self.S1.SEC[self.S1.SEC['KSI']]
|
|
secctx['UL_enb'] = self._nas_rx._ulcnt
|
|
except Exception:
|
|
pass
|
|
if self.EMM.require_smc(self):
|
|
return self._ret_smc(self.UEInfo['NAS_KSI'])
|
|
else:
|
|
# otherwise, go directly to postprocess
|
|
return self.postprocess()
|
|
|
|
def _process_comp(self):
|
|
#
|
|
if self.mtmsi_realloc >= 0:
|
|
self.UE.set_mtmsi(self.mtmsi_realloc)
|
|
if self.att_type == 2:
|
|
self.UE.set_tmsi(self.mtmsi_realloc)
|
|
self._log('INF', 'new M-TMSI and TMSI set, 0x%.8x' % self.mtmsi_realloc)
|
|
else:
|
|
self._log('INF', 'new M-TMSI set, 0x%.8x' % self.mtmsi_realloc)
|
|
#
|
|
# transfer to the ESM stack, which will terminate the ongoing ESM procedure
|
|
# and shoud return an empty list
|
|
ret = self.S1.ESM.process_buf(self.CompInfo['ESMContainer'].get_val(),
|
|
sec=self._nas_rx._sec)
|
|
ret.extend(self._end())
|
|
return ret
|
|
|
|
def postprocess(self, Proc=None):
|
|
if isinstance(Proc, EMMIdentification):
|
|
assert( Proc.IDType == NAS.IDTYPE_IMSI)
|
|
# got the UE's IMSI, check if it's allowed
|
|
if self.UE.IMSI is None:
|
|
# UE did actually not responded with its IMSI, this is bad !
|
|
# error 96: invalid mandatory info
|
|
self.errcause = 96
|
|
return self.output()
|
|
elif not self._chk_imsi():
|
|
return self.output()
|
|
if self.EMM.require_auth(self, ksi=self.UEInfo['NAS_KSI']):
|
|
return self._ret_auth()
|
|
else:
|
|
try:
|
|
secctx = self.S1.SEC[self.S1.SEC['KSI']]
|
|
secctx['UL_enb'] = self._nas_rx._ulcnt
|
|
except Exception:
|
|
pass
|
|
if self.EMM.require_smc(self):
|
|
return self._ret_smc(self.UEInfo['NAS_KSI'])
|
|
#
|
|
elif isinstance(Proc, EMMAuthentication):
|
|
if not Proc.success:
|
|
self.abort()
|
|
return []
|
|
elif self.EMM.require_smc(self):
|
|
# ksi established during the auth procedure
|
|
return self._ret_smc(Proc.ksi)
|
|
#
|
|
elif isinstance(Proc, EMMSecurityModeControl):
|
|
if not Proc.success:
|
|
self.abort()
|
|
return []
|
|
#
|
|
elif Proc != self and Proc is not None:
|
|
self._err = Proc
|
|
assert()
|
|
#
|
|
return self.output()
|
|
|
|
def output(self):
|
|
if self.att_type == 2 and self.EMM.ATT_IMSI:
|
|
# IMSI attach not supported
|
|
self.errcause = self.EMM.ATT_IMSI
|
|
#
|
|
if self.errcause:
|
|
# prepare AttachReject IE
|
|
if self.errcause == self.EMM.ATT_IMSI_PROV_REJECT:
|
|
self.set_msg(7, 68, EMMCause=self.errcause, T3346=self.EMM.ATT_T3346)
|
|
else:
|
|
self.set_msg(7, 68, EMMCause=self.errcause)
|
|
self.encode_msg(7, 68)
|
|
if not self._nas_rx._sec:
|
|
self._nas_tx._sec = False
|
|
self.mtmsi_realloc = -1
|
|
self._log('INF', 'reject, %r' % self._nas_tx['EMMCause'])
|
|
ret = self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
ret.extend( self._end() )
|
|
else:
|
|
#
|
|
# prepare the TAIList for the UE:
|
|
# it only contains a single PartialTAIList of type 0 with the TAI of the eNB
|
|
# to which the UE is connected
|
|
tailist = [{'Type':0, 'PLMN':self.UE.PLMN, 'TACValues':[self.UE.TAC]}]
|
|
#
|
|
# prepare AttachAccept IEs
|
|
IEs = {'EPSAttachResult': self.att_type,
|
|
'T3412' : self.EMM.ATT_T3412,
|
|
'TAIList' : tailist,
|
|
}
|
|
#
|
|
# in case we want to realloc a GUTI, we start a GUTIRealloc,
|
|
# but don't forward its output
|
|
if self.EMM.ATT_GUTI_REALLOC:
|
|
NasProc = self.EMM.init_proc(EMMGUTIReallocation)
|
|
NasProc.output(embedded=True)
|
|
if NasProc.guti is not None:
|
|
IEs['GUTI'] = {'type': NAS.IDTYPE_GUTI, 'ident': NasProc.guti}
|
|
self.mtmsi_realloc = NasProc.mtmsi
|
|
else:
|
|
self.mtmsi_realloc = -1
|
|
else:
|
|
self.mtmsi_realloc = -1
|
|
#
|
|
if self.att_type == 2:
|
|
# combined attachment: we set the TAC as the LAC
|
|
self.UE.LAC = self.UE.TAC
|
|
if self.mtmsi_realloc >= 0:
|
|
# including a TMSI realloc: we set the M-TMSI as CS TMSI
|
|
IEs['LAI'] = {'PLMN': self.UE.PLMN, 'LAC': self.UE.LAC}
|
|
IEs['ID'] = {'type': NAS.IDTYPE_TMSI, 'ident': self.mtmsi_realloc}
|
|
#
|
|
if self.EMM.ATT_T3402 is not None:
|
|
IEs['T3402'] = self.EMM.ATT_T3402
|
|
if self.S1.Config['EquivPLMNList'] is not None:
|
|
IEs['EquivPLMNList'] = self.S1.Config['EquivPLMNList']
|
|
if isinstance(self.S1.Config['EmergNumList'], bytes_types):
|
|
IEs['EmergNumList'] = self.S1.Config['EmergNumList']
|
|
elif self.S1.Config['EmergNumList'] is not None:
|
|
IEs['EmergNumList'] = [{'ServiceCat': {c:1 for c in cat}, 'Num': num} for \
|
|
(cat, num) in self.S1.Config['EmergNumList']]
|
|
#
|
|
if self.EMM.ATT_EPS_NETFEAT_SUPP:
|
|
IEs['EPSNetFeat'] = self.EMM.ATT_EPS_NETFEAT_SUPP
|
|
#
|
|
# power saving mode
|
|
if 'MSNetFeatSupp' in self.UEInfo and self.UEInfo['MSNetFeatSupp'][1].get_val():
|
|
if self.EMM.ATT_T3412_EXT:
|
|
IEs['T3412Ext'] = self.EMM.ATT_T3412_EXT
|
|
elif 'T3412Ext' in self.UEInfo:
|
|
IEs['T3412Ext'] = self.UEInfo['T3412Ext'].get_val()
|
|
if 'T3324' in self.UEInfo:
|
|
if self.EMM.ATT_T3324:
|
|
IEs['T3324'] = self.EMM.ATT_T3324
|
|
else:
|
|
IEs['T3324'] = self.UEInfo['T3324'].get_val()
|
|
#
|
|
if 'ExtDRXParam' in self.UEInfo:
|
|
if self.EMM.ATT_EXTDRX:
|
|
IEs['ExtDRXParam'] = self.EMM.ATT_EXTDRX
|
|
else:
|
|
IEs['ExtDRXParam'] = self['ExtDRXParam'].get_val()
|
|
#
|
|
if self.EMM.ATT_SMS_SERV_STAT:
|
|
IEs['SMSServStat'] = self.EMM.ATT_SMS_SERV_STAT
|
|
#
|
|
self.set_msg(7, 66, **IEs)
|
|
self.encode_msg(7, 66)
|
|
#
|
|
# Transfer the UE ESMContainer to the ESM stack, which will populate
|
|
# the ESMContainer in the AttachAccept and setup the proper S1AP procedure.
|
|
# In case of ESM failure, the ESM procedure handler will set self.errcause
|
|
# to 19 (ESM failure) and rebuild an AttachReject through its ._EMMProc
|
|
# attribute.
|
|
ret = self.S1.ESM.process_buf(self.UEInfo['ESMContainer'].get_val(),
|
|
sec=self._sec,
|
|
EMMProc=self)
|
|
#
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
if self.errcause:
|
|
ret.extend( self._end() )
|
|
else:
|
|
self.init_timer()
|
|
return ret
|
|
|
|
def _end(self):
|
|
ret = []
|
|
if self.EMM.ATT_S1REL or self.errcause and self.EMM.ATT_S1REL_ONERR:
|
|
S1apProcRel = self.S1.init_s1ap_proc(S1APUEContextRelease, Cause=('nas', 'normal-release'))
|
|
if S1apProcRel:
|
|
ret.append(S1apProcRel)
|
|
self.rm_from_emm_stack()
|
|
return ret
|
|
|
|
|
|
class EMMDetachUE(EMMSigProc):
|
|
"""Detach procedure: TS 24.301, section 5.5.2
|
|
|
|
UE-initiated
|
|
|
|
CN message:
|
|
EMMDetachAccept (PD 7, Type 70), IEs:
|
|
None
|
|
|
|
UE message:
|
|
EMMDetachRequestMO (PD 7, Type 69), IEs:
|
|
- Type1V : NAS_KSI
|
|
- Type1V : EPSDetachType
|
|
- Type4LV : EPSID
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMDetachAccept, ),
|
|
(TS24301_EMM.EMMDetachRequestMO, )
|
|
)
|
|
|
|
def _detach(self):
|
|
if self.S1.SEC['KSI'] is not None and not self._nas_rx._sec:
|
|
# security is activated, but the detach request is not protected
|
|
# this is not acceptable
|
|
self._log('WNG', 'invalid detach request with no security layer')
|
|
else:
|
|
# set EMM state
|
|
self.EMM.state = 'INACTIVE'
|
|
self._log('INF', 'detaching')
|
|
#
|
|
self.rm_from_emm_stack()
|
|
# abort all ongoing EPS procedures and PDN ctxt
|
|
self.S1.clear_nas_proc()
|
|
self.S1.ESM.pdn_clear()
|
|
#
|
|
if self.UE.IMSI is None:
|
|
# UEd was created based on a S-TMSI provided at the RRC layer
|
|
# just delete it
|
|
try:
|
|
del self.UE.Server._UEpre[self.UE.MTMSI]
|
|
except Exception:
|
|
pass
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
self.emm_preempt()
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
#
|
|
if self.UEInfo['EPSDetachType']['SwitchOff'].get_val():
|
|
# if UE is to power-off, procedure ends here
|
|
ret = self.output(poff=True)
|
|
else:
|
|
# TODO: implement require_auth() / require_smc()
|
|
#
|
|
ret = self.output(poff=False)
|
|
self._detach()
|
|
return ret
|
|
|
|
def output(self, poff=False):
|
|
# prepare a stack of S1AP procedure(s)
|
|
S1apTxProc = []
|
|
if not poff:
|
|
# set a S1ap direct transfer to transport the DetachAccept
|
|
#self.set_msg(7, 70)
|
|
self.encode_msg(7, 70)
|
|
if not self._nas_rx._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
S1apProc = self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
if S1apProc:
|
|
S1apTxProc.extend( S1apProc )
|
|
S1apProcRel = self.S1.init_s1ap_proc(S1APUEContextRelease, Cause=('nas', 'detach'))
|
|
if S1apProcRel:
|
|
S1apTxProc.append( S1apProcRel )
|
|
return S1apTxProc
|
|
|
|
|
|
class EMMDetachCN(EMMSigProc):
|
|
"""Detach procedure: TS 24.301, section 5.5.2
|
|
|
|
CN-initiated
|
|
|
|
CN message:
|
|
EMMDetachRequestMT (PD 7, Type 69), IEs:
|
|
- Type1V : spare
|
|
- Type1V : EPSDetachType
|
|
- Type3TV : EMMCause
|
|
|
|
UE message:
|
|
EMMDetachAccept (PD 7, Type 70), IEs:
|
|
None
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMDetachRequestMT, ),
|
|
(TS24301_EMM.EMMDetachAccept, )
|
|
)
|
|
|
|
Init = (7, 69)
|
|
Timer = 'T3422'
|
|
|
|
def _detach(self):
|
|
if self.S1.SEC['KSI'] is not None and not self._nas_rx._sec:
|
|
# security is activated, but the detach request is not protected
|
|
# this is not acceptable
|
|
self._log('WNG', 'invalid detach response with no security layer')
|
|
else:
|
|
# set EMM state
|
|
self.EMM.state = 'INACTIVE'
|
|
self._log('INF', 'detaching')
|
|
#
|
|
self.rm_from_emm_stack()
|
|
# abort all ongoing EPS procedures and PDN ctxt
|
|
self.S1.clear_nas_proc()
|
|
self.S1.ESM.pdn_clear()
|
|
|
|
def output(self):
|
|
self.encode_msg(7, 69)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self._log('INF', self._nas_tx['EPSDetachType'].repr())
|
|
# in case of IMSI-detach, a TAU with IMSI attach is expected in response
|
|
if self._nas_tx['EPSDetachType']['Type'].get_val() != 3:
|
|
self.init_timer()
|
|
else:
|
|
self.rm_from_emm_stack()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
|
|
def process(self, pdu):
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
self._detach()
|
|
S1apProcRel = self.S1.init_s1ap_proc(S1APUEContextRelease, Cause=('nas', 'detach'))
|
|
if S1apProcRel:
|
|
return [S1apProcRel]
|
|
return []
|
|
|
|
|
|
class EMMTrackingAreaUpdate(EMMSigProc):
|
|
"""Tracking area updating procedure: TS 24.301, section 5.5.3
|
|
|
|
UE-initiated
|
|
|
|
CN messages:
|
|
EMMTrackingAreaUpdateAccept (PD 7, Type 73), IEs:
|
|
- Type1V : spare
|
|
- Type1V : EPSUpdateResult
|
|
- Type3TV : T3412
|
|
- Type4TLV : GUTI
|
|
- Type4TLV : TAIList
|
|
- Type4TLV : EPSBearerCtxtStat
|
|
- Type3TV : LAI
|
|
- Type4TLV : ID
|
|
- Type3TV : EMMCause
|
|
- Type3TV : T3402
|
|
- Type3TV : T3423
|
|
- Type4TLV : EquivPLMNList
|
|
- Type4TLV : EmergNumList
|
|
- Type4TLV : EPSNetFeat
|
|
- Type1TV : AddUpdateRes
|
|
- Type4TLV : T3412Ext
|
|
- Type4TLV : T3324
|
|
- Type4TLV : ExtDRXParam
|
|
- Type4TLV : HdrCompConfigStat
|
|
- Type1TV : SMSServStat
|
|
|
|
EMMTrackingAreaUpdateReject (PD 7, Type 75), IEs:
|
|
- Type3V : EMMCause
|
|
- Type4TLV : T3346
|
|
- Type1TV : ExtEMMCause
|
|
|
|
UE messages:
|
|
EMMTrackingAreaUpdateRequest (PD 7, Type 72), IEs:
|
|
- Type1V : NAS_KSI
|
|
- Type1V : EPSUpdateType
|
|
- Type4LV : OldGUTI
|
|
- Type1TV : Native_NAS_KSI
|
|
- Type1TV : GPRS_CKSN
|
|
- Type3TV : OldPTMSISign
|
|
- Type4TLV : AddGUTI
|
|
- Type3TV : NonceUE
|
|
- Type4TLV : UENetCap
|
|
- Type3TV : OldTAI
|
|
- Type3TV : DRXParam
|
|
- Type1TV : UERACapUpdateNeed
|
|
- Type4TLV : EPSBearerCtxtStat
|
|
- Type4TLV : MSNetCap
|
|
- Type3TV : OldLAI
|
|
- Type1TV : TMSIStatus
|
|
- Type4TLV : MSCm2
|
|
- Type4TLV : MSCm3
|
|
- Type4TLV : SuppCodecs
|
|
- Type1TV : AddUpdateType
|
|
- Type4TLV : VoiceDomPref
|
|
- Type1TV : OldGUTIType
|
|
- Type1TV : DeviceProp
|
|
- Type1TV : MSNetFeatSupp
|
|
- Type4TLV : TMSIBasedNRICont
|
|
- Type4TLV : T3324
|
|
- Type4TLV : T3412Ext
|
|
- Type4TLV : ExtDRXParam
|
|
|
|
EMMTrackingAreaUpdateComplete (PD 7, Type 74), IEs:
|
|
None
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMTrackingAreaUpdateAccept, TS24301_EMM.EMMTrackingAreaUpdateReject),
|
|
(TS24301_EMM.EMMTrackingAreaUpdateRequest, TS24301_EMM.EMMTrackingAreaUpdateComplete)
|
|
)
|
|
|
|
Decod = {
|
|
(7, 72): {
|
|
'NAS_KSI' : lambda x: (x[0][0].get_val(), x[0][1].get_val()),
|
|
'OldGUTI' : lambda x: x[1].decode(),
|
|
'OldTAI' : lambda x: x[1].decode(),
|
|
'OldLAI' : lambda x: x[1].decode(),
|
|
},
|
|
}
|
|
|
|
Cap = ('UENetCap', 'DRXParam', 'MSNetCap', 'MSCm2', 'MSCm3', 'SuppCodecs',
|
|
'VoiceDomPref', 'DeviceProp', 'MSNetFeatSupp', 'ExtDRXParam')
|
|
|
|
Timer = 'T3450'
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
self.emm_preempt()
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
#
|
|
if pdu._name == 'EMMTrackingAreaUpdateRequest':
|
|
self.errcause, self.UEInfo = None, {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
return self._process_req()
|
|
else:
|
|
# EMMTrackingAreaUpdateComplete
|
|
self.errcause, self.CompInfo = None, {}
|
|
self.decode_msg(pdu, self.CompInfo)
|
|
return self._process_comp()
|
|
|
|
def _process_req(self):
|
|
#
|
|
upd_type = self.UEInfo['EPSUpdateType']
|
|
self.upd_type = upd_type.get_val()
|
|
if self.upd_type[1] in (1, 2):
|
|
# combined TA / LA update
|
|
self.upd_res = 1
|
|
if 'TMSIStatus' not in self.UEInfo and 'TMSIBasedNRICont' not in self.UEInfo:
|
|
self._log('INF', 'combined TA/LA requested, but not TMSIStatus neither '\
|
|
'TMSIBasedNRICont provided')
|
|
else:
|
|
self.upd_res = 0
|
|
self._log('INF', upd_type.repr())
|
|
# collect capabilities
|
|
self._collect_cap()
|
|
#
|
|
# check local ID
|
|
if self.UE.IMSI is None:
|
|
# UEd was created based on a S-TMSI provided at the RRC layer
|
|
# -> we need to get its IMSI before continuing
|
|
try:
|
|
del self.UE.Server._UEpre[self.UE.MTMSI]
|
|
except Exception:
|
|
pass
|
|
# need to request the IMSI, prepare an id request procedure
|
|
return self._ret_req_imsi()
|
|
#
|
|
elif self.EMM.require_auth(self, ksi=self.UEInfo['NAS_KSI']):
|
|
return self._ret_auth()
|
|
else:
|
|
# no auth procedure, ksi submitted by the UE is valid
|
|
# set UL NAS count for further KeNB derivation
|
|
try:
|
|
secctx = self.S1.SEC[self.S1.SEC['KSI']]
|
|
secctx['UL_enb'] = self._nas_rx._ulcnt
|
|
except Exception:
|
|
pass
|
|
if self.EMM.require_smc(self):
|
|
return self._ret_smc(self.UEInfo['NAS_KSI'])
|
|
else:
|
|
# otherwise, go directly to postprocess
|
|
return self.postprocess()
|
|
|
|
def _process_comp(self):
|
|
if self.mtmsi_realloc >= 0:
|
|
self.UE.set_mtmsi(self.mtmsi_realloc)
|
|
if self.upd_res in (1, 5):
|
|
self.UE.set_tmsi(self.mtmsi_realloc)
|
|
self._log('INF', 'new M-TMSI and TMSI set, 0x%.8x' % self.mtmsi_realloc)
|
|
else:
|
|
self._log('INF', 'new M-TMSI set, 0x%.8x' % self.mtmsi_realloc)
|
|
return self._end()
|
|
|
|
def postprocess(self, Proc=None):
|
|
if isinstance(Proc, EMMIdentification):
|
|
assert( Proc.IDType == NAS.IDTYPE_IMSI)
|
|
# got the UE's IMSI, check if it's allowed
|
|
if self.UE.IMSI is None:
|
|
# UE did actually not responded with its IMSI, this is bad !
|
|
# error 96: invalid mandatory info
|
|
self.errcause = 96
|
|
return self.output()
|
|
elif not self._chk_imsi():
|
|
return self.output()
|
|
if self.EMM.require_auth(self, ksi=self.UEInfo['NAS_KSI']):
|
|
return self._ret_auth()
|
|
else:
|
|
try:
|
|
secctx = self.S1.SEC[self.S1.SEC['KSI']]
|
|
secctx['UL_enb'] = self._nas_rx._ulcnt
|
|
except Exception:
|
|
pass
|
|
if self.EMM.require_smc(self):
|
|
return self._ret_smc(self.UEInfo['NAS_KSI'])
|
|
#
|
|
elif isinstance(Proc, EMMAuthentication):
|
|
if not Proc.success:
|
|
self.abort()
|
|
return []
|
|
elif self.EMM.require_smc(self):
|
|
# ksi established during the auth procedure
|
|
return self._ret_smc(Proc.ksi)
|
|
#
|
|
elif isinstance(Proc, EMMSecurityModeControl):
|
|
if not Proc.success:
|
|
self.abort()
|
|
return []
|
|
#
|
|
elif Proc != self and Proc is not None:
|
|
self._err = Proc
|
|
assert()
|
|
#
|
|
return self.output()
|
|
|
|
def output(self):
|
|
if self.errcause:
|
|
# prepare TAUReject IE
|
|
self.set_msg(7, 75, EMMCause=self.errcause)
|
|
self.encode_msg(7, 75)
|
|
if not self._nas_rx._sec:
|
|
self._nas_tx._sec = False
|
|
self.mtmsi_realloc = -1
|
|
self._log('INF', 'reject, %r' % self._nas_tx['EMMCause'])
|
|
ret = self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
self.BearActProc = None
|
|
ret.extend(self._end())
|
|
else:
|
|
# prepare TAU Accept IEs
|
|
IEs = {'EPSUpdateResult': self.upd_res}
|
|
#
|
|
# check EPSBearerCtxtStat, and deactivate PDN not enabled at the UE
|
|
if 'EPSBearerCtxtStat' in self.UEInfo:
|
|
EBCtxtStat = self.UEInfo['EPSBearerCtxtStat']
|
|
EBCtxtStatResp = []
|
|
for Stat in EBCtxtStat:
|
|
uestat = Stat.get_val()
|
|
ebi = int(Stat._name[4:])
|
|
if uestat == 1 and ebi not in self.S1.ESM.PDN:
|
|
self._log('WNG', 'EPS bearer %i activated in the UE but not the network' % ebi)
|
|
EBCtxtStatResp.append(0)
|
|
elif uestat == 0 and ebi in self.S1.ESM.PDN:
|
|
self._log('INF', 'EPS bearer %i activated in the network but not the UE' % ebi)
|
|
pdncfg = self.S1.ESM.PDN[ebi]
|
|
if pdncfg['state'] == 1:
|
|
self.UE.Server.GTPUd.rem_mobile(pdncfg['RAB']['SGW-GTP-TEID'])
|
|
del self.S1.ESM.PDN[ebi]
|
|
EBCtxtStatResp.append(0)
|
|
else:
|
|
EBCtxtStatResp.append(uestat)
|
|
IEs['EPSBearerCtxtStat'] = EBCtxtStatResp
|
|
#
|
|
# check UERACapUpdateNeed, and delete UERadCap if needed
|
|
if 'UERACapUpdateNeed' in self.UEInfo \
|
|
and self.UEInfo['UERACapUpdateNeed'].get_val() \
|
|
and 'UERadioCap' in self.UE.Cap:
|
|
del self.UE.Cap['UERadioCap']
|
|
#
|
|
if self.EMM.TAU_T3402 is not None:
|
|
IEs['T3402'] = self.EMM.TAU_T3402
|
|
if self.EMM.TAU_T3412 is not None:
|
|
IEs['T3412'] = self.EMM.TAU_T3412
|
|
#
|
|
# in case we want to realloc a GUTI, we start a GUTIRealloc,
|
|
# but don't forward its output
|
|
if self.EMM.TAU_GUTI_REALLOC:
|
|
NasProc = self.EMM.init_proc(EMMGUTIReallocation)
|
|
NasProc.output(embedded=True)
|
|
if NasProc.guti is not None:
|
|
IEs['GUTI'] = {'type': NAS.IDTYPE_GUTI, 'ident': NasProc.guti}
|
|
self.mtmsi_realloc = NasProc.mtmsi
|
|
else:
|
|
self.mtmsi_realloc = -1
|
|
else:
|
|
self.mtmsi_realloc = -1
|
|
#
|
|
if self.upd_res in (1, 5):
|
|
# combined TAU / LAU: we set the TAC as the LAC
|
|
self.UE.LAC = self.UE.TAC
|
|
if self.mtmsi_realloc >= 0:
|
|
# including a TMSI realloc: we set the M-TMSI as CS TMSI
|
|
IEs['LAI'] = {'PLMN': self.UE.PLMN, 'LAC': self.UE.LAC}
|
|
IEs['ID'] = {'type': NAS.IDTYPE_TMSI, 'ident': self.mtmsi_realloc}
|
|
#
|
|
# power saving mode
|
|
if 'MSNetFeatSupp' in self.UEInfo and self.UEInfo['MSNetFeatSupp'][1].get_val():
|
|
if self.EMM.ATT_T3412_EXT:
|
|
IEs['T3412Ext'] = self.EMM.ATT_T3412_EXT
|
|
elif 'T3412Ext' in self.UEInfo:
|
|
IEs['T3412Ext'] = self.UEInfo['T3412Ext'].get_val()
|
|
if 'T3324' in self.UEInfo:
|
|
if self.EMM.ATT_T3324:
|
|
IEs['T3324'] = self.EMM.ATT_T3324
|
|
else:
|
|
IEs['T3324'] = self.UEInfo['T3324'].get_val()
|
|
#
|
|
if 'ExtDRXParam' in self.UEInfo:
|
|
if self.EMM.ATT_EXTDRX:
|
|
IEs['ExtDRXParam'] = self.EMM.ATT_EXTDRX
|
|
else:
|
|
IEs['ExtDRXParam'] = self['ExtDRXParam'].get_val()
|
|
#
|
|
if self.S1.Config['EquivPLMNList'] is not None:
|
|
IEs['EquivPLMNList'] = self.S1.Config['EquivPLMNList']
|
|
if isinstance(self.S1.Config['EmergNumList'], bytes_types):
|
|
IEs['EmergNumList'] = self.S1.Config['EmergNumList']
|
|
elif self.S1.Config['EmergNumList'] is not None:
|
|
IEs['EmergNumList'] = [{'ServiceCat': {c:1 for c in cat}, 'Num': num} for \
|
|
(cat, num) in self.S1.Config['EmergNumList']]
|
|
if self.EMM.TAU_EPS_NETFEAT_SUPP:
|
|
IEs['EPSNetFeat'] = self.EMM.TAU_EPS_NETFEAT_SUPP
|
|
if self.EMM.TAU_SMS_SERV_STAT:
|
|
IEs['SMSServStat'] = self.EMM.TAU_SMS_SERV_STAT
|
|
#
|
|
self._IEs = IEs
|
|
self.set_msg(7, 73, **IEs)
|
|
self.encode_msg(7, 73)
|
|
#
|
|
if self.upd_type[0]:
|
|
# reactivate EPS bearers
|
|
self.BearActProc = self.S1.bearer_act()
|
|
else:
|
|
self.BearActProc = None
|
|
#
|
|
ret = self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
if self.BearActProc:
|
|
ret.append(self.BearActProc)
|
|
if self.mtmsi_realloc >= 0:
|
|
self.init_timer()
|
|
else:
|
|
ret.extend(self._end())
|
|
#
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
return ret
|
|
|
|
def _end(self):
|
|
ret = []
|
|
if self.EMM.TAU_S1REL and not self.BearActProc:
|
|
S1apProcRel = self.S1.init_s1ap_proc(S1APUEContextRelease, Cause=('nas', 'normal-release'))
|
|
if S1apProcRel:
|
|
ret.append(S1apProcRel)
|
|
self.rm_from_emm_stack()
|
|
return ret
|
|
|
|
|
|
#------------------------------------------------------------------------------#
|
|
# EMM connection management procedures (S1 mode only): TS 24.301, section 5.6
|
|
#------------------------------------------------------------------------------#
|
|
|
|
class EMMServiceRequest(EMMSigProc):
|
|
"""Service request procedure: TS 24.301, section 5.6.1
|
|
|
|
UE-initiated
|
|
|
|
CN message:
|
|
None
|
|
|
|
UE message:
|
|
EMMServiceRequest (SH 12, PD 7), IEs:
|
|
None
|
|
"""
|
|
|
|
Cont = (
|
|
None,
|
|
(TS24301_EMM.EMMServiceRequest, )
|
|
)
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
self.emm_preempt()
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.errcause, self.UEInfo = None, {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
#
|
|
# check local ID
|
|
if self.UE.IMSI is None:
|
|
# UEd was created based on a S-TMSI provided at the RRC layer
|
|
# -> we need to get its IMSI before continuing
|
|
try:
|
|
del self.UE.Server._UEpre[self.UE.MTMSI]
|
|
except Exception:
|
|
pass
|
|
# need to request the IMSI, prepare an id request procedure
|
|
return self._ret_req_imsi()
|
|
#
|
|
elif self.EMM.require_auth(self, ksi=(0, self.UEInfo['KSI'].get_val())):
|
|
return self._ret_auth()
|
|
else:
|
|
# no auth procedure, ksi submitted by the UE is valid
|
|
# set UL NAS count for further KeNB derivation
|
|
try:
|
|
secctx = self.S1.SEC[self.S1.SEC['KSI']]
|
|
secctx['UL_enb'] = self._nas_rx._ulcnt
|
|
except Exception:
|
|
pass
|
|
if self.EMM.require_smc(self) and self.EMM.SER_SMC_ALW:
|
|
return self._ret_smc((0, self.UEInfo['KSI'].get_val()))
|
|
else:
|
|
# otherwise, go directly to postprocess
|
|
return self.postprocess()
|
|
|
|
def postprocess(self, Proc=None):
|
|
if isinstance(Proc, EMMIdentification):
|
|
assert( Proc.IDType == NAS.IDTYPE_IMSI)
|
|
# got the UE's IMSI, check if it's allowed
|
|
if self.UE.IMSI is None:
|
|
# UE did actually not responded with its IMSI, this is bad !
|
|
# error 96: invalid mandatory info
|
|
self.errcause = 96
|
|
return self.output()
|
|
elif not self._chk_imsi():
|
|
return self.output()
|
|
if self.EMM.require_auth(self, ksi=(0, self.UEInfo['KSI'].get_val())):
|
|
return self._ret_auth()
|
|
else:
|
|
try:
|
|
secctx = self.S1.SEC[self.S1.SEC['KSI']]
|
|
secctx['UL_enb'] = self._nas_rx._ulcnt
|
|
except Exception:
|
|
pass
|
|
if self.EMM.require_smc(self) and self.EMM.SER_SMC_ALW:
|
|
return self._ret_smc((0, self.UEInfo['KSI'].get_val()))
|
|
#
|
|
elif isinstance(Proc, EMMAuthentication):
|
|
if not Proc.success:
|
|
self.abort()
|
|
return []
|
|
elif self.EMM.require_smc(self):
|
|
# ksi established during the auth procedure
|
|
return self._ret_smc(Proc.ksi)
|
|
#
|
|
elif isinstance(Proc, EMMSecurityModeControl):
|
|
if not Proc.success:
|
|
self.abort()
|
|
return []
|
|
#
|
|
elif Proc != self and Proc is not None:
|
|
self._err = Proc
|
|
assert()
|
|
#
|
|
return self.output()
|
|
|
|
def output(self):
|
|
self.rm_from_emm_stack()
|
|
if self.errcause:
|
|
self._nas_tx = NAS.EMMStatus(EMMCause=self.errcause)
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
#
|
|
else:
|
|
if not self.S1.ESM.PDN:
|
|
if self.EMM.SER_PDN_ALW:
|
|
esmd = self.S1.ESM
|
|
# in case no PDN were activated, create a minimal one
|
|
if esmd.APN_DEFAULT not in esmd.PDNConfig:
|
|
self._log('INF', 'no PDN config available')
|
|
return []
|
|
pdncfg = esmd.PDNConfig[esmd.APN_DEFAULT]
|
|
# always use the IPv4 address only
|
|
ipaddr, err = esmd._get_pdn_addr(pdncfg, 1)
|
|
if err:
|
|
return []
|
|
esmd.rab_set_default(5, self.S1.ESM.APN_DEFAULT, ipaddr, pdncfg)
|
|
else:
|
|
self._log('WNG', 'no PDN config available')
|
|
return []
|
|
elif self.EMM.SER_RAB_NEVER:
|
|
return []
|
|
# reactivate all PDN connections
|
|
S1apProc = self.S1.bearer_act()
|
|
if S1apProc:
|
|
return [S1apProc]
|
|
else:
|
|
return []
|
|
|
|
|
|
class EMMExtServiceRequest(EMMSigProc):
|
|
"""Extended service request procedure: TS 24.301, section 5.6.1
|
|
|
|
UE-initiated
|
|
|
|
CN message:
|
|
None
|
|
|
|
UE message:
|
|
EMMExtServiceRequest (PD 7, Type 76), IEs:
|
|
- Type1V : NAS_KSI
|
|
- Type1V : ServiceType
|
|
- Type4LV : MTMSI
|
|
- Type1TV : CSFBResponse
|
|
- Type4TLV : EPSBearerCtxtStat
|
|
- Type1TV : DeviceProp
|
|
"""
|
|
|
|
Cont = (
|
|
None,
|
|
(TS24301_EMM.EMMExtServiceRequest, )
|
|
)
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
self.emm_preempt()
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
# TODO
|
|
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
|
|
class EMMCPServiceRequest(EMMSigProc):
|
|
"""Control-Plane service request procedure: TS 24.301, section 5.6.1
|
|
|
|
UE-initiated
|
|
|
|
CN message:
|
|
None
|
|
|
|
UE message:
|
|
EMMCPServiceRequest (PD 7, Type 77), IEs:
|
|
- Type1V : NAS_KSI
|
|
- Type1V : CPServiceType
|
|
- Type6TLVE : ESMContainer
|
|
- Type4TLV : NASContainer
|
|
- Type4TLV : EPSBearerCtxtStat
|
|
- Type1TV : DeviceProp
|
|
"""
|
|
|
|
Cont = (
|
|
None,
|
|
(TS24301_EMM.EMMCPServiceRequest, )
|
|
)
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
self.emm_preempt()
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
# TODO
|
|
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
|
|
class EMMDLNASTransport(EMMSigProc):
|
|
"""Transport of NAS messages procedure: TS 24.301, section 5.6.3
|
|
|
|
CN-initiated
|
|
|
|
CN message:
|
|
EMMDLNASTransport (PD 7, Type 98), IEs:
|
|
- Type4LV : NASContainer
|
|
|
|
UE message:
|
|
None
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMDLNASTransport, ),
|
|
None
|
|
)
|
|
|
|
Init = (7, 98)
|
|
|
|
def output(self):
|
|
self.encode_msg(7, 98)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self.rm_from_emm_stack()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
|
|
|
|
class EMMULNASTransport(EMMSigProc):
|
|
"""Transport of NAS messages procedure: TS 24.301, section 5.6.3
|
|
|
|
UE-initiated
|
|
|
|
CN message:
|
|
None
|
|
|
|
UE message:
|
|
EMMULNASTransport (PD 7, Type 99), IEs:
|
|
- Type4LV : NASContainer
|
|
"""
|
|
|
|
Cont = (
|
|
None,
|
|
(TS24301_EMM.EMMULNASTransport, )
|
|
)
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
#
|
|
# decode the SMS CP layer
|
|
SMSRx, self.errcause = NAS.parse_NAS_MO(self.UEInfo['NASContainer'].get_val())
|
|
if not self.errcause and SMSRx[0][0]['ProtDisc'].get_val() != 9:
|
|
self.errcause = 96
|
|
if self.errcause:
|
|
self._nas_tx = NAS.EMMStatus(EMMCause=self.errcause)
|
|
ret = self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
else:
|
|
ret = []
|
|
# transfer the CP message to the SMS stack
|
|
retcp = self.S1.SMS.process(SMSRx)
|
|
for SMSTx in retcp:
|
|
# pack them into EMMDLNASTransport procedure
|
|
NasProc = self.EMM.init_proc(EMMDLNASTransport,
|
|
encod={(7, 98): {'NASContainer': SMSTx.to_bytes()}})
|
|
ret.extend( NasProc.output() )
|
|
self.rm_from_emm_stack()
|
|
return ret
|
|
|
|
|
|
class EMMDLGenericNASTransport(EMMSigProc):
|
|
"""Generic transport of NAS messages procedure: TS 24.301, section 5.6.4
|
|
|
|
CN-initiated
|
|
|
|
CN message:
|
|
EMMDLGenericNASTransport (PD 7, Type 104), IEs:
|
|
- Type3V : GenericContType
|
|
- Type6LVE : GenericContainer
|
|
- Type4TLV : AddInfo
|
|
|
|
UE message:
|
|
None
|
|
"""
|
|
|
|
Cont = (
|
|
(TS24301_EMM.EMMDLGenericNASTransport, ),
|
|
None
|
|
)
|
|
|
|
Init = (7, 104)
|
|
|
|
def output(self):
|
|
self.encode_msg(7, 104)
|
|
if not self._sec:
|
|
self._nas_tx._sec = False
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'DL', self._nas_tx) )
|
|
self.rm_from_emm_stack()
|
|
return self.S1.ret_s1ap_dnt(self._nas_tx)
|
|
|
|
|
|
class EMMULGenericNASTransport(EMMSigProc):
|
|
"""Generic transport of NAS messages procedure: TS 24.301, section 5.6.4
|
|
|
|
UE-initiated
|
|
|
|
CN message:
|
|
None
|
|
|
|
UE message:
|
|
EMMULGenericNASTransport (PD 7, Type 105), IEs:
|
|
- Type3V : GenericContType
|
|
- Type6LVE : GenericContainer
|
|
- Type4TLV : AddInfo
|
|
"""
|
|
|
|
Cont = (
|
|
None,
|
|
(TS24301_EMM.EMMULGenericNASTransport, )
|
|
)
|
|
|
|
def process(self, pdu):
|
|
# preempt the EMM stack
|
|
if self.TRACK_PDU:
|
|
self._pdu.append( (time(), 'UL', pdu) )
|
|
self.UEInfo = {}
|
|
self.decode_msg(pdu, self.UEInfo)
|
|
# TODO
|
|
|
|
self.rm_from_emm_stack()
|
|
return []
|
|
|
|
|
|
EMMGUTIReallocation.init(filter_init=1)
|
|
EMMAuthentication.init(filter_init=1)
|
|
EMMSecurityModeControl.init(filter_init=1)
|
|
EMMIdentification.init(filter_init=1)
|
|
EMMInformation.init(filter_init=1)
|
|
EMMAttach.init(filter_init=1)
|
|
EMMDetachUE.init(filter_init=1)
|
|
EMMDetachCN.init(filter_init=1)
|
|
EMMTrackingAreaUpdate.init(filter_init=1)
|
|
EMMServiceRequest.init(filter_init=1)
|
|
EMMExtServiceRequest.init(filter_init=1)
|
|
EMMCPServiceRequest.init(filter_init=1)
|
|
EMMDLNASTransport.init(filter_init=1)
|
|
EMMULNASTransport.init(filter_init=1)
|
|
EMMDLGenericNASTransport.init(filter_init=1)
|
|
EMMULGenericNASTransport.init(filter_init=1)
|
|
|
|
# EMM UE-initiated procedures dispatcher
|
|
EMMProcUeDispatcher = {
|
|
0 : EMMServiceRequest,
|
|
65 : EMMAttach,
|
|
69 : EMMDetachUE,
|
|
72 : EMMTrackingAreaUpdate,
|
|
76 : EMMExtServiceRequest,
|
|
77 : EMMCPServiceRequest,
|
|
99 : EMMULNASTransport,
|
|
105 : EMMULGenericNASTransport
|
|
}
|
|
EMMProcUeDispatcherStr = {ProcClass.Cont[1][0]()._name: ProcClass \
|
|
for ProcClass in EMMProcUeDispatcher.values()}
|
|
|
|
# EMM CN-initiated procedures dispatcher
|
|
EMMProcCnDispatcher = {
|
|
69 : EMMDetachCN,
|
|
80 : EMMGUTIReallocation,
|
|
82 : EMMAuthentication,
|
|
85 : EMMIdentification,
|
|
93 : EMMSecurityModeControl,
|
|
97 : EMMInformation,
|
|
98 : EMMDLNASTransport,
|
|
104 : EMMDLGenericNASTransport
|
|
}
|
|
EMMProcCnDispatcherStr = {ProcClass.Cont[0][0]()._name: ProcClass \
|
|
for ProcClass in EMMProcCnDispatcher.values()}
|
|
|