def post(self, *args, **kwargs): """Create a new tenant. Args: [0], the tenant id Request: version: protocol version (1.0) owner: the username of the requester tenant_name: the network name desc: a description for the new tenant bssid_type: shared or unique plmn_id: the PLMN id """ bssid_type = kwargs["bssid_type"] \ if "bssid_type" in kwargs else T_TYPE_UNIQUE plmn_id = PLMNID(kwargs["plmn_id"]) if "plmn_id" in kwargs else None tenant_id = UUID(args[0]) if args else None RUNTIME.add_tenant(kwargs['owner'], kwargs['desc'], kwargs['tenant_name'], bssid_type, tenant_id, plmn_id) self.set_header("Location", "/api/v1/tenants/%s" % tenant_id)
def _handle_ran_mac_slice_response(self, vbs, hdr, event, msg): """Handle an incoming RAN_MAC_SLICE message. Args: status, a RAN_MAC_SLICE messagge Returns: None """ dscp = DSCP(msg.dscp) plmn_id = PLMNID(msg.plmn_id) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) # check if tenant is valid if not tenant: self.log.info("Unknown tenant %s", plmn_id) return # check if slice is valid if dscp not in tenant.slices: self.log.warning("DSCP %s not found. Removing slice.", dscp) # self.send_del_slice(valid[0], ssid, dscp) return slc = tenant.slices[dscp] if vbs.addr not in slc.lte['vbses']: slc.lte['vbses'][vbs.addr] = \ {'static-properties': {}, 'runtime-properties': {}, 'cells': {}} if hdr.cellid not in slc.lte['vbses'][vbs.addr]['cells']: slc.lte['vbses'][vbs.addr]['cells'][hdr.cellid] = {} for raw_cap in msg.options: if raw_cap.type not in RAN_MAC_SLICE_TYPES: self.log.warning("Unknown options %u", raw_cap.type) continue prop = RAN_MAC_SLICE_TYPES[raw_cap.type].name option = RAN_MAC_SLICE_TYPES[raw_cap.type].parse(raw_cap.data) self.log.warning("Processing options %s", prop) if raw_cap.type == EP_RAN_MAC_SLICE_SCHED_ID: slc.lte['vbses'][vbs.addr] \ ['static-properties']['sched_id'] = option.sched_id if raw_cap.type == EP_RAN_MAC_SLICE_RBGS: slc.lte['vbses'][vbs.addr]['static-properties']['rbgs'] = \ option.rbgs if raw_cap.type == EP_RAN_MAC_SLICE_RNTI_LIST: slc.lte['vbses'][vbs.addr]['runtime-properties']['rntis'] = \ option.rntis self.log.info("Slice %s updated", slc)
def _handle_ue_report_response(self, vbs, hdr, event, ue_report): """Handle an incoming UE_REPORT message. Args: hello, a UE_REPORT message Returns: None """ incoming = [] for ue in ue_report.ues: if RUNTIME.find_ue_by_rnti(ue.rnti, ue.pci, vbs): continue plmn_id = PLMNID(ue.plmn_id[1:].hex()) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) if not tenant: self.log.info("Unable to find PLMN id %s", plmn_id) continue if vbs.addr not in tenant.vbses: self.log.info("VBS %s not in PLMN id %s", vbs.addr, plmn_id) continue cell = vbs.get_cell_by_pci(ue.pci) if not cell: self.log.info("PCI %u not found", u.pci) continue if ue.imsi != 0: ue_id = uuid.uuid5(uuid.NAMESPACE_DNS, str(ue.imsi)) else: ue_id = uuid.uuid4() ue = UE(ue_id, ue.imsi, ue.rnti, cell, plmn_id, tenant) ue.set_active() RUNTIME.ues[ue.ue_id] = ue tenant.ues[ue.ue_id] = ue incoming.append(ue.ue_id) self.server.send_ue_join_message_to_self(ue) # check for leaving UEs for ue_id in list(RUNTIME.ues.keys()): if RUNTIME.ues[ue_id].vbs != vbs: continue if not RUNTIME.ues[ue_id].is_active(): self.log.info("Handover in progress for %u, ignoring", ue_id) continue if ue_id not in incoming: RUNTIME.remove_ue(ue_id)
def _handle_ue_report_response(self, vbs, hdr, event, msg): """Handle an incoming UE_REPORT message. Args: hello, a UE_REPORT message Returns: None """ for raw_entry in msg.options: if raw_entry.type not in UE_REPORT_TYPES: self.log.warning("Unknown options %u", raw_entry) continue prop = UE_REPORT_TYPES[raw_entry.type].name option = UE_REPORT_TYPES[raw_entry.type].parse(raw_entry.data) self.log.warning("Processing options %s", prop) if raw_entry.type == EP_UE_REPORT_IDENTITY: plmn_id = PLMNID(option.plmn_id) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) if not tenant: self.log.info("Unknown tenant %s", plmn_id) continue # TODO: Implement fallback mechanism IMSI->TMSI->RNTI ue_id = uuid.UUID(int=option.imsi) # UE already known, update its parameters if ue_id in RUNTIME.ues: ue = RUNTIME.ues[ue_id] ue.rnti = option.rnti # UE is disconnecting if option.state == 1: RUNTIME.remove_ue(ue_id) else: cell = vbs.cells[hdr.cellid] ue = UE(ue_id, option.rnti, option.imsi, option.timsi, cell, tenant) RUNTIME.ues[ue.ue_id] = ue tenant.ues[ue.ue_id] = ue # UE is connected if option.state == 0: self.server.send_ue_join_message_to_self(ue)
def _handle_ue_report_response(self, vbs, hdr, event, ue_report): """Handle an incoming UE_REPORT message. Args: hello, a UE_REPORT message Returns: None """ ues = {u.imsi: u for u in ue_report.ues} # check for new UEs for u in ues.values(): # UE already known if u.imsi in RUNTIME.ues: continue plmn_id = PLMNID(u.plmn_id[1:].hex()) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) if not tenant: self.log.info("Unable to find PLMN id %s", plmn_id) continue if vbs.addr not in tenant.vbses: self.log.info("VBS %s not in PLMN id %s", vbs.addr, plmn_id) continue cell = None for c in vbs.cells: if c.pci == u.pci: cell = c break if not cell: self.log.info("PCI %u not found", u.pci) continue ue = UE(u.imsi, u.rnti, cell, plmn_id, tenant) ue.set_active() RUNTIME.ues[u.imsi] = ue tenant.ues[u.imsi] = ue self.server.send_ue_join_message_to_self(ue) # check for leaving UEs for imsi in list(RUNTIME.ues.keys()): if RUNTIME.ues[imsi].vbs != vbs: continue if imsi not in ues: RUNTIME.remove_ue(imsi)
def _handle_ue_report_response(self, vbs, hdr, event, ue_report): """Handle an incoming UE_REPORT message. Args: hello, a UE_REPORT message Returns: None """ LOG.info("UE report from %s VBS %s seq %u", self.addr[0], vbs.addr, hdr.seq) ues = {u.imsi: u for u in ue_report.ues} for u in ues.values(): plmn_id = PLMNID(u.plmn_id[1:].hex()) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) if not tenant: LOG.info("Unable to find PLMN id %s", plmn_id) continue if vbs.addr not in tenant.vbses: LOG.info("VBS %s not in PLMN id %s", vbs.addr, plmn_id) continue cell = None for c in vbs.cells: if c.pci == u.pci: cell = c if not cell: LOG.info("PCI %u not found", u.pci) continue ue = UE(u.imsi, u.rnti, cell, plmn_id, tenant) new_ue = False if u.imsi not in RUNTIME.ues: new_ue = True RUNTIME.ues[u.imsi] = ue tenant.ues[u.imsi] = ue if new_ue: self.server.send_ue_join_message_to_self(ue) for ue in RUNTIME.ues.values(): if ue.imsi not in ues: self.server.send_ue_leave_message_to_self(ue)
def send_ran_mac_slice_request(self, cell_id, plmn_id=PLMNID(), dscp=DSCP()): """Send a STATUS_SLICE_REQUEST message. Args: None Returns: None """ msg = Container(length=RAN_MAC_SLICE_REQUEST.sizeof(), plmn_id=plmn_id.to_raw(), dscp=dscp.to_raw(), padding=b'\x00\x00\x00') self.send_message(msg, E_TYPE_SINGLE, EP_ACT_RAN_MAC_SLICE, RAN_MAC_SLICE_REQUEST, cellid=cell_id)
def _handle_ran_mac_slice_response(self, vbs, hdr, event, msg): """Handle an incoming RAN_MAC_SLICE message. Args: status, a RAN_MAC_SLICE messagge Returns: None """ dscp = DSCP(msg.dscp) plmn_id = PLMNID(msg.plmn_id) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) # check if tenant is valid if not tenant: self.log.info("Unknown tenant %s", plmn_id) return # check if slice is valid if dscp not in tenant.slices: self.log.warning("DSCP %s not found. Removing slice.", dscp) cell = vbs.cells[hdr.cellid] self.send_del_ran_mac_slice_request(cell, plmn_id, dscp) return slc = tenant.slices[dscp] for raw_cap in msg.options: if raw_cap.type not in RAN_MAC_SLICE_TYPES: self.log.warning("Unknown options %u", raw_cap.type) continue prop = RAN_MAC_SLICE_TYPES[raw_cap.type].name option = RAN_MAC_SLICE_TYPES[raw_cap.type].parse(raw_cap.data) self.log.warning("Processing options %s", prop) if raw_cap.type == EP_RAN_MAC_SLICE_SCHED_ID: if option.sched_id != slc.lte['static-properties']['sched_id']: if vbs.addr not in slc.lte['vbses']: slc.lte['vbses'][vbs.addr] = { 'static-properties': {} } slc.lte['vbses'][vbs.addr] \ ['static-properties']['sched_id'] = option.sched_id if raw_cap.type == EP_RAN_MAC_SLICE_RBGS: if option.rbgs != slc.lte['static-properties']['rbgs']: if vbs.addr not in slc.lte['vbses']: slc.lte['vbses'][vbs.addr] = { 'static-properties': {} } slc.lte['vbses'][vbs.addr] \ ['static-properties']['rbgs'] = option.rbgs if raw_cap.type == EP_RAN_MAC_SLICE_RNTI_LIST: rntis = option.rntis for ue in list(tenant.ues.values()): if ue.vbs != vbs: continue # if the UE was attached to this slice, but it is not # in the information given by the eNB, it should be # deleted. if slc.dscp in ue.slices and ue.rnti not in rntis: ue.remove_slice(slc.dscp) # if the UE was not attached to this slice, but its RNTI # is provided by the eNB for this slice, it should added. if slc.dscp not in ue.slices and ue.rnti in rntis: ue.add_slice(slc.dscp) self.log.info("Slice %s updated", slc)
def _handle_ue_report_response(self, vbs, hdr, event, msg): """Handle an incoming UE_REPORT message. Args: hello, a UE_REPORT message Returns: None """ for raw_entry in msg.options: if raw_entry.type not in UE_REPORT_TYPES: self.log.warning("Unknown options %u", raw_entry) continue prop = UE_REPORT_TYPES[raw_entry.type].name option = UE_REPORT_TYPES[raw_entry.type].parse(raw_entry.data) self.log.warning("Processing options %s", prop) if raw_entry.type == EP_UE_REPORT_IDENTITY: # NOTE: These ID generation should fallback to a data-type like for PLMNID imsi_id = uuid.UUID(int=option.imsi) tmsi_id = uuid.UUID(int=option.tmsi) # VBS can have multiple carriers (cells), and each carrier can allocate # its own RNTI range independently. This means that on UUID generation # by RNTI you can get multiple different UEs with the same UUID if only # RNTI is considered. This gives to the ID a little of context. rnti_id = uuid.UUID(int=vbs.addr.to_int() << 32 | hdr.cellid << 16 | option.rnti) plmn_id = PLMNID(option.plmn_id) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) if not tenant: self.log.info("Unknown tenant %s", plmn_id) continue # Basic fallback mechanism for UE unique ID generation # # IMSI # UE ID is generated using the Subscriber Identity, thus it # will remain stable through multiple connection/disconnection if option.imsi != 0: ue_id = imsi_id # TMSI # UE ID is generated using Temporary ID assigned by the Core # Network, and will be stable depending on the CN ID generation # behavior elif option.tmsi != 0: ue_id = tmsi_id # RNTI # UE ID is generated using the Radio Network Temporary # Identifier. This means that at any event where such identifier # is changed update, the UE ID will potentially will change too else: ue_id = rnti_id # UE already known, update its parameters if ue_id in RUNTIME.ues: ue = RUNTIME.ues[ue_id] # RNTI must always be set, but just in case handle the event if option.rnti != 0: ue.rnti = option.rnti else: self.log.info("UE is missing RNTI identifier!") continue # Update the TMSI if has been renew for some reason if option.tmsi != 0: ue.tmsi = option.tmsi # Fill IMSI only if it was not previously set if option.imsi != 0 and ue.imsi != 0: ue.imsi = option.imsi # UE is disconnecting if option.state == 1: RUNTIME.remove_ue(ue_id) # UE not known else: # Reporting on and entry which switched to offline; ignore if option.state == 1: continue cell = vbs.cells[hdr.cellid] ue = UE(ue_id, option.rnti, option.imsi, option.tmsi, cell, tenant) RUNTIME.ues[ue.ue_id] = ue tenant.ues[ue.ue_id] = ue # UE is connected if option.state == 0: self.server.send_ue_join_message_to_self(ue)
def post(self, *args, **kwargs): """ Create a new tenant request. Args: None Request: version: protocol version (1.0) owner: the username of the requester tenant_id: the network name desc: a description for the new tenant bssid_type: shared or unique Example URLs: POST /api/v1/pending """ try: if len(args) > 1: raise ValueError("Invalid url") request = tornado.escape.json_decode(self.request.body) if "version" not in request: raise ValueError("missing version element") if "desc" not in request: raise ValueError("missing desc element") if "tenant_name" not in request: raise ValueError("missing tenant_name element") bssid_type = T_TYPE_UNIQUE if "bssid_type" in request: bssid_type = request['bssid_type'] if "plmn_id" in request: plmn_id = PLMNID(request['plmn_id']) else: plmn_id = None if len(args) == 1: tenant_id = UUID(args[0]) else: tenant_id = None tenant_name = SSID(request['tenant_name']) RUNTIME.request_tenant(self.account.username, request['desc'], tenant_name, bssid_type, tenant_id, plmn_id) self.set_header("Location", "/api/v1/pendig/%s" % tenant_id) except KeyError as ex: self.send_error(404, message=ex) except ValueError as ex: self.send_error(400, message=ex) self.set_status(201, None)
def _handle_ue_report_response(self, vbs, hdr, event, msg): """Handle an incoming UE_REPORT message. Args: hello, a UE_REPORT message Returns: None """ for raw_entry in msg.options: if raw_entry.type not in UE_REPORT_TYPES: self.log.warning("Unknown options %u", raw_entry.raw_entry) continue prop = UE_REPORT_TYPES[raw_entry.type].name option = UE_REPORT_TYPES[raw_entry.type].parse(raw_entry.data) self.log.warning("Processing options %s", prop) if raw_entry.type == EP_UE_REPORT_IDENTITY: plmn_id = PLMNID(option.plmn_id) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) if not tenant: self.log.info("Unknown tenant %s", plmn_id) continue ue = RUNTIME.find_ue_by_rnti(option.rnti, hdr.cellid, vbs) # UE already known, update its parameters if ue: ue.plmn_id = plmn_id ue.tmsi = option.timsi else: cell = vbs.cells[hdr.cellid] ue_id = uuid.uuid4() ue = UE(ue_id, option.rnti, option.imsi, option.timsi, cell, tenant) RUNTIME.ues[ue.ue_id] = ue tenant.ues[ue.ue_id] = ue self.server.send_ue_join_message_to_self(ue) elif raw_entry.type == EP_UE_REPORT_STATE: ue_id = uuid.uuid4() ue = RUNTIME.find_ue_by_rnti(option.rnti, hdr.cellid, vbs) if not ue: continue try: ue.state = UE_REPORT_STATES[option.state] except IOError: self.log.error("Invalid transistion %s -> %s" \ %(ue.state, UE_REPORT_STATES[option.state]))