corenet: continue support for gNB
This commit is contained in:
parent
55628841d0
commit
f1f73b7a8d
|
@ -4,6 +4,7 @@
|
|||
# * Version : 0.4
|
||||
# *
|
||||
# * Copyright 2017. Benoit Michau. ANSSI.
|
||||
# * Copyright 2020. Benoit Michau. P1Sec.
|
||||
# *
|
||||
# * This library is free software; you can redistribute it and/or
|
||||
# * modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -31,8 +32,9 @@
|
|||
# This is the main corenet server
|
||||
#
|
||||
# It serves connection to:
|
||||
# - Home-NodeB over HNBAP and RUA / RANAP
|
||||
# - eNodeB and Home-eNodeB over S1AP
|
||||
# - Home-NodeB over HNBAP and RUA
|
||||
# - gNodeB over NGAP
|
||||
#
|
||||
# It handles signalling trafic for UE
|
||||
# and connects them to specific service handler (SMS, GTPU, ...)
|
||||
|
@ -41,6 +43,7 @@
|
|||
from .utils import *
|
||||
from .HdlrHNB import HNBd
|
||||
from .HdlrENB import ENBd
|
||||
from .HdlrGNB import GNBd
|
||||
from .HdlrUE import UEd
|
||||
from .ServerAuC import AuC
|
||||
from .ServerGTPU import ARPd, GTPUd, BLACKHOLE_LAN, BLACKHOLE_WAN
|
||||
|
@ -124,12 +127,32 @@ class CorenetServer(object):
|
|||
#
|
||||
# main PLMN served
|
||||
PLMN = '00101'
|
||||
# equivalent PLMNs served, used for Iu and S1 interface
|
||||
# None or list of PLMNs ['30124', '763326', ...]
|
||||
EQUIV_PLMN = None
|
||||
#
|
||||
# AMF RegionID, SetID and Pointer
|
||||
AMF_RID = 1
|
||||
AMF_SID = 1
|
||||
AMF_PTR = 0
|
||||
# list of PLMN and slices supported
|
||||
# a sliceSupportList is basically a Sequence of S-NSSAI
|
||||
AMF_SNSSAI = {
|
||||
1 : {'sST': b'\x01'},
|
||||
2 : {'sST': b'\x02'},
|
||||
21 : {'sST': b'\x02', 'sD': b'\0\0\x01'},
|
||||
}
|
||||
AMF_PLMNSupp = [
|
||||
#{'pLMNIdentity': $plmn1,
|
||||
# 'sliceSupportList': [{'s-NSSAI': AMF_SNSSAI[1]}]},
|
||||
#{'pLMNIdentity': $plmn2,
|
||||
# 'sliceSupportList': [{'s-NSSAI': AMF_SNSSAI[2]}, {'s-NSSAI': AMF_SNSSAI[21]}]},
|
||||
]
|
||||
#
|
||||
# MME GroupID and Code
|
||||
MME_GID = 1
|
||||
MME_CODE = 1
|
||||
# equivalent PLMNs served
|
||||
# None or list of PLMNs ['30124', '763326', ...]
|
||||
EQUIV_PLMN = None
|
||||
#
|
||||
# emergency number lists
|
||||
# None or list of 2-tuple [(number_category, number), ...]
|
||||
# number_category is a set of strings: 'Police', 'Ambulance', 'Fire', 'Marine', 'Mountain'
|
||||
|
@ -140,6 +163,21 @@ class CorenetServer(object):
|
|||
# ({'Marine', 'Mountain'}, '112113')]
|
||||
EMERG_NUMS = None
|
||||
#
|
||||
# N2 connection AMF parameters
|
||||
ConfigN2 = {
|
||||
'AMFName' : 'CorenetAMF',
|
||||
'PLMNSupportList' : AMF_PLMNSupp,
|
||||
'RelativeAMFCapacity': 10,
|
||||
'ServedGUAMIList' : [
|
||||
{'gUAMI': {
|
||||
'pLMNIdentity': plmn_str_to_buf(PLMN),
|
||||
'aMFRegionID' : (AMF_RID, 8),
|
||||
'aMFSetID' : (AMF_SID, 10),
|
||||
'aMFPointer' : (AMF_PTR, 6)}},
|
||||
],
|
||||
#'UERetentionInformation': 'ues-retained',
|
||||
}
|
||||
#
|
||||
# S1 connection MME parameters
|
||||
ConfigS1 = {
|
||||
'MMEname': 'CorenetMME',
|
||||
|
@ -152,6 +190,7 @@ class CorenetServer(object):
|
|||
'EquivPLMNList' : EQUIV_PLMN,
|
||||
'EmergNumList' : EMERG_NUMS,
|
||||
}
|
||||
#
|
||||
# HNBAP connection GW parameters (keep it empty)
|
||||
ConfigHNBAP = {}
|
||||
# RUA connection GW parameters (keep it empty)
|
||||
|
@ -313,25 +352,14 @@ class CorenetServer(object):
|
|||
#--------------------------------------------------------------------------#
|
||||
|
||||
def start(self, serving=True):
|
||||
# start SCTP servers, bind() and listen()
|
||||
self.SCTPServ = [] # will be casted to tuple
|
||||
#
|
||||
if DEBUG_SK:
|
||||
self._skc = []
|
||||
# LUT for connected SCTP client and ENBId / HNBId
|
||||
self.SCTPCli = {}
|
||||
#
|
||||
if self.SERVER_HNB:
|
||||
self._start_hnb_server()
|
||||
self.SCTPServ.append( self._sk_hnb )
|
||||
else:
|
||||
self._sk_hnb = None
|
||||
#
|
||||
if self.SERVER_ENB:
|
||||
self._start_enb_server()
|
||||
self.SCTPServ.append( self._sk_enb )
|
||||
else:
|
||||
self._sk_enb = None
|
||||
self.SCTPServ = tuple(self.SCTPServ)
|
||||
# start SCTP servers, bind() and listen()
|
||||
self._start_server()
|
||||
#
|
||||
# init the dict for storing UE with unknown IMSI at attachment
|
||||
self._UEpre = {}
|
||||
|
@ -369,45 +397,30 @@ class CorenetServer(object):
|
|||
def is_running(self):
|
||||
return self._running
|
||||
|
||||
def _start_hnb_server(self):
|
||||
# start SCTP server for Home-NodeBs
|
||||
server_addr = (self.SERVER_HNB['IP'], self.SERVER_HNB['port'])
|
||||
try:
|
||||
self._sk_hnb = sctp.sctpsocket_tcp(self.SERVER_HNB['INET'])
|
||||
self.sctp_set_events(self._sk_hnb)
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot create SCTP socket: {0}'.format(err)))
|
||||
try:
|
||||
self._sk_hnb.bind(server_addr)
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot bind SCTP socket on address {0!r}: {1}'\
|
||||
.format(server_addr, err)))
|
||||
try:
|
||||
self._sk_hnb.listen(self.SERVER_HNB['MAXCLI'])
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot listen to SCTP connection: {1}'.format(err)))
|
||||
#
|
||||
self._log('INF', 'SCTP HNB server started on address %r' % (server_addr, ))
|
||||
|
||||
def _start_enb_server(self):
|
||||
# start SCTP server for eNodeBs
|
||||
server_addr = (self.SERVER_ENB['IP'], self.SERVER_ENB['port'])
|
||||
try:
|
||||
self._sk_enb = sctp.sctpsocket_tcp(self.SERVER_ENB['INET'])
|
||||
self.sctp_set_events(self._sk_enb)
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot create SCTP socket: {0}'.format(err)))
|
||||
try:
|
||||
self._sk_enb.bind(server_addr)
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot bind SCTP socket on address {0!r}: {1}'\
|
||||
.format(server_addr, err)))
|
||||
try:
|
||||
self._sk_enb.listen(self.SERVER_ENB['MAXCLI'])
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot listen to SCTP connection: {1}'.format(err)))
|
||||
#
|
||||
self._log('INF', 'SCTP ENB server started on address %r' % (server_addr, ))
|
||||
def _start_server(self):
|
||||
self.SCTPServ = []
|
||||
for (cfg, attr) in ((self.SERVER_HNB, '_sk_hnb',
|
||||
(self.SERVER_ENB, '_sk_enb',
|
||||
(self.SERVER_GNB, '_sk_gnb')):
|
||||
if 'INET' not in cfg or 'IP' not in cfg \
|
||||
or 'port' not in cfg or 'MAXCLI' not in cfg:
|
||||
setattr(self, attr, None)
|
||||
continue
|
||||
try:
|
||||
sk = sctp.sctpsocket_tcp(cfg['INET'])
|
||||
addr = (cfg['IP'], cfg['port'])
|
||||
srv = attr[-3:].upper()
|
||||
self.sctp_set_events(sk)
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot create SCTP socket: %s' % err))
|
||||
try:
|
||||
sk.bind(addr)
|
||||
except Exception as err:
|
||||
raise(CorenetErr('cannot bind SCTP socket on addr %r: %s' % (addr, err)))
|
||||
self._log('INF', 'SCTP %s server started on address %r' % (srv, addr))
|
||||
setattr(self, attr, sk)
|
||||
self.SCTPServ.append(sk)
|
||||
self.SCTPServ = tuple(self.SCTPServ)
|
||||
|
||||
def _serve(self):
|
||||
# Main server loop, using select() to read sockets, the loop:
|
||||
|
@ -424,7 +437,10 @@ class CorenetServer(object):
|
|||
self._running = False
|
||||
#
|
||||
for sk in skr:
|
||||
if sk == self._sk_enb:
|
||||
if sk == self._sk_gnb:
|
||||
# new gNodeB SCTP client (NGSetupRequest)
|
||||
self.handle_new_gnb()
|
||||
elif sk == self._sk_enb:
|
||||
# new eNodeB STCP client (S1SetupRequest)
|
||||
self.handle_new_enb()
|
||||
elif sk == self._sk_hnb:
|
||||
|
@ -445,15 +461,18 @@ class CorenetServer(object):
|
|||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
asn_ngap_release()
|
||||
asn_s1ap_release()
|
||||
asn_hnbap_release()
|
||||
asn_rua_release()
|
||||
asn_ranap_release()
|
||||
sleep(self.SCHED_RES + 0.01)
|
||||
if hasattr(self, '_sk_hnb') and self._sk_hnb:
|
||||
if self._sk_hnb is not None:
|
||||
self._sk_hnb.close()
|
||||
if hasattr(self, '_sk_enb') and self._sk_enb:
|
||||
if self._sk_enb is not None:
|
||||
self._sk_enb.close()
|
||||
if self._sk_gnb is not None:
|
||||
self._sk_gnb.close()
|
||||
self._clean_ue_proc.join()
|
||||
#
|
||||
# disconnect all RAN clients
|
||||
|
@ -534,6 +553,11 @@ class CorenetServer(object):
|
|||
# remove from the Server location tables
|
||||
if cli.Config:
|
||||
self._unset_enb_loc(cli)
|
||||
elif isinstance(cli, GNBd):
|
||||
self._log('DBG', 'gNB %s closed connection' % (cli.ID,))
|
||||
# remove from the Server location tables
|
||||
if cli.Config:
|
||||
self._unset_gnb_loc(cli)
|
||||
else:
|
||||
assert()
|
||||
# update HNB / ENB state
|
||||
|
@ -631,8 +655,8 @@ class CorenetServer(object):
|
|||
asn_s1ap_release()
|
||||
enb._log('WNG', 'invalid S1AP PDU transfer-syntax: %s'\
|
||||
% hexlify(buf).decode('ascii'))
|
||||
Err = enb.init_hnbap_proc(S1APErrorIndNonUECN,
|
||||
Cause=('protocol', 'transfer-syntax-error'))
|
||||
Err = enb.init_s1ap_proc(S1APErrorIndNonUECN,
|
||||
Cause=('protocol', 'transfer-syntax-error'))
|
||||
pdu_tx = Err.send()
|
||||
else:
|
||||
pdu_rx = PDU_S1AP()
|
||||
|
@ -648,6 +672,35 @@ class CorenetServer(object):
|
|||
for pdu in pdu_tx:
|
||||
self.send_s1ap_pdu(enb, pdu, sid)
|
||||
#
|
||||
elif ppid == SCTP_PPID_NGAP:
|
||||
assert( isinstance(ran, GNBd) )
|
||||
gnb = ran
|
||||
if not asn_ngap_acquire():
|
||||
gnb._log('ERR', 'unable to acquire the NGAP module')
|
||||
return
|
||||
try:
|
||||
PDU_NGAP.from_aper(buf)
|
||||
except:
|
||||
asn_ngap_release()
|
||||
gnb._log('WNG', 'invalid NGAP PDU transfer-syntax: %s'\
|
||||
% hexlify(buf).decode('ascii'))
|
||||
Err = gnb.init_ngap_proc(NGAPErrorIndNonUECN,
|
||||
Cause=('protocol', 'transfer-syntax-error'))
|
||||
pdu_tx = Err.send()
|
||||
else:
|
||||
pdu_rx = PDU_S1AP()
|
||||
if enb.TRACE_ASN_NGAP:
|
||||
enb._log('TRACE_ASN_NGAP_UL', PDU_NGAP.to_asn1())
|
||||
asn_ngap_release()
|
||||
if sid == gnb.SKSid:
|
||||
# non-UE-associated signalling
|
||||
pdu_tx = gnb.process_ngap_pdu(pdu_rx)
|
||||
else:
|
||||
# UE-associated signalling
|
||||
pdu_tx = enb.process_ngap_ue_pdu(pdu_rx, sid)
|
||||
for pdu in pdu_tx:
|
||||
self.send_ngap_pdu(gnb, pdu, sid)
|
||||
#
|
||||
else:
|
||||
self._log('ERR', 'invalid SCTP PPID, %i' % ppid)
|
||||
if self.SERVER_HNB['errclo']:
|
||||
|
@ -687,6 +740,161 @@ class CorenetServer(object):
|
|||
asn_s1ap_release()
|
||||
return self._write_sk(enb.SK, buf, ppid=SCTP_PPID_S1AP, stream=sid)
|
||||
|
||||
def send_ngap_pdu(self, gnb, pdu, sid):
|
||||
if not asn_ngap_acquire():
|
||||
gnb._log('ERR', 'unable to acquire the NGAP module')
|
||||
return
|
||||
PDU_NGAP.set_val(pdu)
|
||||
if gnb.TRACE_ASN_NGAP:
|
||||
gnb._log('TRACE_ASN_NGAP_DL', PDU_NGAP.to_asn1())
|
||||
buf = PDU_NGAP.to_aper()
|
||||
asn_ngap_release()
|
||||
return self._write_sk(gnb.SK, buf, ppid=SCTP_PPID_NGAP, stream=sid)
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# gNodeB connection
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
def _parse_ngsetup(self, pdu):
|
||||
if pdu[0] != 'initiatingMessage' or pdu[1]['procedureCode'] != 21:
|
||||
# not initiating / NGSetup
|
||||
self._log('WNG', 'invalid NGAP PDU for setting up the gNB NGAP link')
|
||||
return
|
||||
#
|
||||
pIEs, plmn, ranid = pdu[1]['value'][1], None, None
|
||||
IEs = pIEs['protocolIEs']
|
||||
if 'protocolExtensions' in pIEs:
|
||||
Exts = pIEs['protocolExtensions']
|
||||
else:
|
||||
Exts = []
|
||||
for ie in IEs:
|
||||
if ie['id'] == 27:
|
||||
# GlobalRANNodeID:
|
||||
# PLMN,
|
||||
# ID type (gNB-ID, macroNgENB-ID, shortMacroNgENB-ID, longMacroNgENB-ID or n3IWF-ID),
|
||||
# ID bit-string value
|
||||
ranid = ngranid_to_hum(ie['value'][1])
|
||||
plmn = ranid[0]
|
||||
ranid = ranid[1:]
|
||||
break
|
||||
if plmn is None or ranid is None:
|
||||
self._log('WNG', 'invalid NGAP PDU for setting up the gNB NGAP link: '\
|
||||
'missing PLMN and RAN-ID')
|
||||
return
|
||||
# decode PLMN and CellID
|
||||
return plmn, ranid
|
||||
|
||||
def _send_ngsetuprej(self, sk, cause):
|
||||
IEs = [{'criticality': 'ignore',
|
||||
'id': 15, # id-Cause
|
||||
'value': (('NGAP-IEs', 'Cause'), cause)}]
|
||||
pdu = ('unsuccessfulOutcome',
|
||||
{'criticality': 'ignore',
|
||||
'procedureCode': 21,
|
||||
'value': (('NGAP-PDU-Contents', 'NGSetupFailure'),
|
||||
{'protocolIEs' : IEs})})
|
||||
if not asn_ngap_acquire():
|
||||
self._log('ERR', 'unable to acquire the NGAP module')
|
||||
else:
|
||||
PDU_NGAP.set_val(pdu)
|
||||
if GNBd.TRACE_ASN_NGAP:
|
||||
self._log('TRACE_ASN_NGAP_DL', PDU_NGAP.to_asn1())
|
||||
self._write_sk(sk, PDU_NGAP.to_aper(), ppid=SCTP_PPID_NGAP, stream=0)
|
||||
asn_ngap_release()
|
||||
if self.SERVER_GNB['errclo']:
|
||||
sk.close()
|
||||
|
||||
def handle_new_gnb(self):
|
||||
sk, addr = self._sk_gnb.accept()
|
||||
self._log('DBG', 'New gNB client from address %r' % (addr, ))
|
||||
#
|
||||
buf, notif = self._read_sk(sk)
|
||||
if not buf:
|
||||
# WNG: maybe required to handle SCTP notification, at some point
|
||||
return
|
||||
# verifying SCTP Payload Protocol ID and setting stream ID for
|
||||
# non-UE-associated trafic
|
||||
ppid, sid = ntohl(notif.ppid), notif.stream
|
||||
if ppid != SCTP_PPID_NGAP:
|
||||
self._log('ERR', 'invalid NGAP PPID, %i' % ppid)
|
||||
if self.SERVER_GNB['errclo']:
|
||||
sk.close()
|
||||
return
|
||||
#
|
||||
if not asn_ngap_acquire():
|
||||
self._log('ERR', 'unable to acquire the NGAP module')
|
||||
return
|
||||
try:
|
||||
PDU_NGAP.from_aper(buf)
|
||||
except:
|
||||
self._log('WNG', 'invalid NGAP PDU transfer-syntax: %s'\
|
||||
% hexlify(buf).decode('ascii'))
|
||||
# return nothing, no need to bother
|
||||
return
|
||||
if GNBd.TRACE_ASN_NGAP:
|
||||
self._log('TRACE_ASN_NGAP_UL', PDU_NGAP.to_asn1())
|
||||
pdu_rx = PDU_NGAP()
|
||||
asn_ngap_release()
|
||||
#
|
||||
GNBId = self._parse_ngsetup(pdu_rx)
|
||||
if GNBId is None:
|
||||
# send NGSetupReject
|
||||
self._send_ngsetuprej(sk, cause=('protocol', 'abstract-syntax-error-reject'))
|
||||
return
|
||||
elif GNBId not in self.RAN:
|
||||
if not self.RAN_CONNECT_ANY:
|
||||
self._log('ERR', 'gNB %r not allowed to connect' % (GNBId,))
|
||||
# send NGSetupReject
|
||||
self._send_ngsetuprej(sk, cause=('radioNetwork', 'unspecified'))
|
||||
return
|
||||
elif GNBId[0] not in self.RAN_ALLOWED_PLMN:
|
||||
self._log('ERR', 'gNB %r not allowed to connect, bad PLMN' % (GNBId,))
|
||||
self._send_ngsetuprej(sk, cause=('radioNetwork', 'unspecified'))
|
||||
return
|
||||
else:
|
||||
# creating an entry for this gNB
|
||||
gnb = GNBd(self, sk, sid)
|
||||
self.RAN[GNBId] = gnb
|
||||
else:
|
||||
if self.RAN[GNBId] is None:
|
||||
# gNB allowed, but not yet connected
|
||||
gnb = GNBd(self, sk, sid)
|
||||
self.RAN[GNBId] = gnb
|
||||
elif not self.RAN[GNBId].is_connected():
|
||||
# gNB already connected and disconnected in the past
|
||||
gnb = self.RAN[GNBId]
|
||||
gnb.__init__(self, sk, sid)
|
||||
else:
|
||||
# gNB already connected
|
||||
self._log('ERR', 'gNB %r already connected from address %r'\
|
||||
% (GNBId, self.RAN[GNBId].SK.getpeername()))
|
||||
if self.SERVER_GNB['errclo']:
|
||||
sk.close()
|
||||
return
|
||||
#
|
||||
# process the initial PDU
|
||||
pdu_tx = gnb.process_ngap_pdu(pdu_rx)
|
||||
# keep track of the client
|
||||
self.SCTPCli[sk] = GNBId
|
||||
# add the gnb TAI to the Server location tables
|
||||
if gnb.Config:
|
||||
self._set_gnb_loc(gnb)
|
||||
#
|
||||
# send available PDU(s) back
|
||||
if not asn_ngap_acquire():
|
||||
gnb._log('ERR', 'unable to acquire the NGAP module')
|
||||
return
|
||||
for pdu in pdu_tx:
|
||||
PDU_NGAP.set_val(pdu)
|
||||
if GNBd.TRACE_ASN_S1AP:
|
||||
gnb._log('TRACE_ASN_NGAP_DL', PDU_NGAP.to_asn1())
|
||||
self._write_sk(sk, PDU_NGAP.to_aper(), ppid=SCTP_PPID_NGAP, stream=sid)
|
||||
asn_ngap_release()
|
||||
|
||||
# in 5G, gNB are dealing with TA more or less in the same way as in 4G
|
||||
_set_gnb_loc = _set_enb_loc
|
||||
_unset_gnb_loc = _unset_enb_loc
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# eNodeB connection
|
||||
#--------------------------------------------------------------------------#
|
||||
|
@ -696,7 +904,7 @@ class CorenetServer(object):
|
|||
# not initiating / S1Setup
|
||||
self._log('WNG', 'invalid S1AP PDU for setting up the eNB S1AP link')
|
||||
return
|
||||
|
||||
#
|
||||
pIEs, plmn, cellid = pdu[1]['value'][1], None, None
|
||||
IEs = pIEs['protocolIEs']
|
||||
if 'protocolExtensions' in pIEs:
|
||||
|
@ -842,7 +1050,7 @@ class CorenetServer(object):
|
|||
try:
|
||||
self.TAI[tai].remove(enb.ID)
|
||||
except:
|
||||
self._log('ERR', 'ENB not referenced into the TAI table')
|
||||
self._log('ERR', 'RAN node %r not referenced into the TAI table' % (enb.ID,))
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# Home-NodeB connection
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# * Version : 0.4
|
||||
# *
|
||||
# * Copyright 2017. Benoit Michau. ANSSI.
|
||||
# * Copyright 2020. Benoit Michau. P1Sec.
|
||||
# *
|
||||
# * This library is free software; you can redistribute it and/or
|
||||
# * modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -577,6 +578,31 @@ def inet_ntoa_cn(pdntype, buf, dom='EPS'):
|
|||
else:
|
||||
return (pdntype, buf)
|
||||
|
||||
|
||||
def ngranid_to_hum(cho):
|
||||
if cho[0] == 'globalGNB-ID':
|
||||
# std gNB-ID
|
||||
return (
|
||||
plmn_buf_to_str(cho[1]['pLMNIdentity']),
|
||||
cho[1]['gNB-ID'][0],
|
||||
cho[1]['gNB-ID'][1]
|
||||
)
|
||||
elif cho[0] == 'globalNgENB-ID':
|
||||
# std ng-eNB-ID
|
||||
return (
|
||||
plmn_buf_to_str(cho[1]['pLMNIdentity']),
|
||||
cho[1]['ngENB-ID'][0],
|
||||
cho[1]['ngENB-ID'][1]
|
||||
)
|
||||
elif cho[0] == 'globalN3IWF-ID':
|
||||
return (
|
||||
plmn_buf_to_str(cho[1]['pLMNIdentity']),
|
||||
cho[1]['n3IWF-ID'][0],
|
||||
cho[1]['n3IWF-ID'][1]
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# ASN.1 object handling facilities
|
||||
#------------------------------------------------------------------------------#
|
||||
|
|
Loading…
Reference in New Issue