def GetSubscriberData(self, request, context): """ Return the subscription data for the subscriber """ print_grpc( request, self._print_grpc_payload, "Get Subscriber Data Request:", ) sid = SIDUtils.to_str(request) try: response = self._store.get_subscriber_data(sid) # get_sub_profile converts the imsi id to a string prependend with IMSI string, # so strip the IMSI prefix in sid imsi = sid[4:] sub_profile = self._lte_processor.get_sub_profile(imsi) response.non_3gpp.ambr.max_bandwidth_ul = sub_profile.max_ul_bit_rate response.non_3gpp.ambr.max_bandwidth_dl = sub_profile.max_dl_bit_rate response.non_3gpp.ambr.br_unit = apn_pb2.AggregatedMaximumBitrate.BitrateUnitsAMBR.BPS except SubscriberNotFoundError: context.set_details("Subscriber not found: %s" % sid) context.set_code(grpc.StatusCode.NOT_FOUND) response = subscriberdb_pb2.SubscriberData() print_grpc( response, self._print_grpc_payload, "Get Subscriber Data Response:", ) return response
def DeleteSuciProfile(self, request, context): """ DeleteSuciProfile - Deletes suciprofile from the store # noqa: D403 Args: request: SuciProfile context: context Returns: None Raises: RpcError: Key is invalid """ print_grpc( request, self._print_grpc_payload, "Delete SuciProfile Request:", ) if request.home_net_public_key_id not in self.suciprofile_db_dict.keys(): logging.warning( "The home_net_public_key_id:%d is not a valid key," "try again", request.home_net_public_key_id, ) context.set_details("Suciprofile not found") context.set_code(grpc.StatusCode.NOT_FOUND) raise grpc.RpcError("Key is invalid") else: del self.suciprofile_db_dict[request.home_net_public_key_id]
def AddSuciProfile(self, request, context): """ AddSuciProfile - Adds a suciprofile to the store # noqa: D403 Args: request: SuciProfile context: context Returns: None Raises: RpcError: Key already exists """ print_grpc( request, self._print_grpc_payload, "Add SuciProfile Request:", ) if request.home_net_public_key_id in self.suciprofile_db_dict.keys(): logging.warning( "home_net_public_key_id:%d already exist", request.home_net_public_key_id, ) context.set_details("Duplicate suciprofile") context.set_code(grpc.StatusCode.ALREADY_EXISTS) raise grpc.RpcError("Key already exists") self.suciprofile_db_dict[request.home_net_public_key_id] = suci_profile_data( request.protection_scheme, request.home_net_public_key, request.home_net_private_key, )
def ListSuciProfile(self, request, context): """ ListSuciProfile - Returns SuciProfile list # noqa: D403 Args: request: SuciProfile context: context Returns: response: SuciProfileList """ print_grpc( request, self._print_grpc_payload, "List Suciprofile Request:", ) suciprofiles = [] for k, v in self.suciprofile_db_dict.items(): suciprofiles.append( subscriberdb_pb2.SuciProfile( home_net_public_key_id=int(k), protection_scheme=v.protection_scheme, home_net_public_key=v.home_network_public_key, home_net_private_key=v.home_network_private_key, ), ) response = subscriberdb_pb2.SuciProfileList(suci_profiles=suciprofiles) print_grpc( response, self._print_grpc_payload, "List SuciProfile Response:", ) return response
def GetAllEnodebStatus(self, _=None, context=None) -> AllEnodebStatus: print_grpc( Void(), self._print_grpc_payload, "GetAllEnodebStatus Request:", ) all_enb_status = AllEnodebStatus() serial_list = self.state_machine_manager.get_connected_serial_id_list() for enb_serial in serial_list: enb_status = get_single_enb_status( enb_serial, self.state_machine_manager, ) all_enb_status.enb_status_list.add( device_serial=enb_status.device_serial, ip_address=enb_status.ip_address, connected=enb_status.connected, configured=enb_status.configured, opstate_enabled=enb_status.opstate_enabled, rf_tx_on=enb_status.rf_tx_on, rf_tx_desired=enb_status.rf_tx_desired, gps_connected=enb_status.gps_connected, ptp_connected=enb_status.ptp_connected, mme_connected=enb_status.mme_connected, gps_longitude=enb_status.gps_longitude, gps_latitude=enb_status.gps_latitude, fsm_state=enb_status.fsm_state, ) print_grpc( all_enb_status, self._print_grpc_payload, "GetAllEnodebStatus Response:", ) return all_enb_status
async def _send_to_state_service(self, request: DeleteStatesRequest): state_client = self._grpc_client_manager.get_client() try: print_grpc( request, self._print_grpc_payload, "Garbage collector sending to state service", ) response = await grpc_async_wrapper( state_client.DeleteStates.future( request, DEFAULT_GRPC_TIMEOUT, ), ) print_grpc(response, self._print_grpc_payload) except grpc.RpcError as err: logging.error( "GRPC call failed for state deletion: %s", err, extra=EXCLUDE_FROM_ERROR_MONITORING if indicates_connection_error(err) else None, ) else: for redis_dict in self._redis_dicts: for key in redis_dict.garbage_keys(): await self._delete_state_from_redis(redis_dict, key)
def get_all_enb_state( print_grpc_payload: bool = False) -> Optional[Dict[int, int]]: """ Make RPC call to 'GetENBState' method of s1ap service """ try: chan = ServiceRegistry.get_rpc_channel( S1AP_SERVICE_NAME, ServiceRegistry.LOCAL, ) except ValueError: logger.error('Cant get RPC channel to %s', S1AP_SERVICE_NAME) return {} client = S1apServiceStub(chan) try: request = Void() print_grpc( request, print_grpc_payload, "Get All eNB State Request:", ) res = client.GetENBState(request, DEFAULT_GRPC_TIMEOUT) print_grpc( res, print_grpc_payload, "Get All eNB State Response:", ) return res.enb_state_map except grpc.RpcError as err: logger.warning( "GetEnbState error: [%s] %s", err.code(), err.details(), ) return {}
def Reboot(self, request: EnodebIdentity, context=None) -> None: """ Reboot eNodeB """ print_grpc( request, self._print_grpc_payload, "Reboot Request:", ) handler = self._get_handler(request.device_serial) handler.reboot_asap()
def M5GAuthenticationInformation(self, request, context): print_grpc( request, self._print_grpc_payload, "M5GAuthenticationInformation Request:", ) imsi = request.user_name aia = subscriberauth_pb2.M5GAuthenticationInformationAnswer() try: re_sync_info = request.resync_info # resync_info = # rand + auts, rand is of 16 bytes + auts is of 14 bytes sizeof_resync_info = 30 if re_sync_info and (re_sync_info != b'\x00' * sizeof_resync_info): rand = re_sync_info[:16] auts = re_sync_info[16:] self.lte_processor.resync_lte_auth_seq(imsi, rand, auts) m5g_ran_auth_vectors = \ self.lte_processor.generate_m5g_auth_vector( imsi, request.serving_network_name.encode( 'utf-8', ), ) metrics.M5G_AUTH_SUCCESS_TOTAL.inc() # Generate and return response message aia.error_code = subscriberauth_pb2.SUCCESS m5gauth_vector = aia.m5gauth_vectors.add() m5gauth_vector.rand = bytes(m5g_ran_auth_vectors.rand) m5gauth_vector.xres_star = m5g_ran_auth_vectors.xres_star[16:] m5gauth_vector.autn = m5g_ran_auth_vectors.autn m5gauth_vector.kseaf = m5g_ran_auth_vectors.kseaf return aia except CryptoError as e: logging.error("Auth error for %s: %s", imsi, e) metrics.M5G_AUTH_FAILURE_TOTAL.labels( code=metrics.DIAMETER_AUTHENTICATION_REJECTED, ).inc() aia.error_code = metrics.DIAMETER_AUTHENTICATION_REJECTED return aia except SubscriberNotFoundError as e: logging.warning("Subscriber not found: %s", e) metrics.M5G_AUTH_FAILURE_TOTAL.labels( code=metrics.DIAMETER_ERROR_USER_UNKNOWN, ).inc() aia.error_code = metrics.DIAMETER_ERROR_USER_UNKNOWN return aia finally: print_grpc( aia, self._print_grpc_payload, "M5GAuthenticationInformation Response:", )
def PurgeUE(self, request, context): logging.warning( "Purge request not implemented: %s %s", request.DESCRIPTOR.full_name, MessageToJson(request), ) pur = s6a_proxy_pb2.PurgeUEAnswer() print_grpc(pur, self._print_grpc_payload, "PUR:") return pur
def RebootAll(self, _=None, context=None) -> None: """ Reboot all connected eNodeB devices """ print_grpc( Void(), self._print_grpc_payload, "RebootAll Request:", ) serial_list = self.state_machine_manager.get_connected_serial_id_list() for enb_serial in serial_list: handler = self._get_handler(enb_serial) handler.reboot_asap()
def DeleteSubscriber(self, request, context): """ Delete a subscriber from the store """ print_grpc( request, self._print_grpc_payload, "Delete Subscriber Request:", ) sid = SIDUtils.to_str(request) logging.debug("Delete subscriber rpc for sid: %s", sid) self._store.delete_subscriber(sid)
async def _send_to_state_service(self, request: ReportStatesRequest): state_client = self._grpc_client_manager.get_client() try: print_grpc( request, self._print_grpc_payload, "Sending to state service", ) response = await grpc_async_wrapper( state_client.ReportStates.future( request, DEFAULT_GRPC_TIMEOUT, ), self._loop, ) print_grpc(response, self._print_grpc_payload) except grpc.RpcError as err: logging.error( "GRPC call failed for state replication: %s", err, extra=EXCLUDE_FROM_ERROR_MONITORING if indicates_connection_error(err) else None, ) else: unreplicated_states = set() for idAndError in response.unreportedStates: logging.warning( "Failed to replicate state for (%s,%s): %s", idAndError.type, idAndError.deviceID, idAndError.error, ) unreplicated_states.add((idAndError.type, idAndError.deviceID)) # Update in-memory map for successfully reported states for state in request.states: if (state.type, state.deviceID) in unreplicated_states: continue in_mem_key = make_mem_key(state.deviceID, state.type) self._state_versions[in_mem_key] = state.version logging.debug( "Successfully replicated state for: " "deviceID: %s," "type: %s, " "version: %d", state.deviceID, state.type, state.version, ) finally: # reset timeout to config-specified + some buffer self.set_timeout(self._interval * 2)
def AuthenticationInformation(self, request, context): print_grpc(request, self._print_grpc_payload, "AIR:") imsi = request.user_name aia = s6a_proxy_pb2.AuthenticationInformationAnswer() try: plmn = request.visited_plmn re_sync_info = request.resync_info # resync_info = # rand + auts, rand is of 16 bytes + auts is of 14 bytes sizeof_resync_info = 30 if re_sync_info and (re_sync_info != b'\x00' * sizeof_resync_info): rand = re_sync_info[:16] auts = re_sync_info[16:] self.lte_processor.resync_lte_auth_seq(imsi, rand, auts) rand, xres, autn, kasme = \ self.lte_processor.generate_lte_auth_vector(imsi, plmn) metrics.S6A_AUTH_SUCCESS_TOTAL.inc() # Generate and return response message aia.error_code = s6a_proxy_pb2.SUCCESS eutran_vector = aia.eutran_vectors.add() eutran_vector.rand = bytes(rand) eutran_vector.xres = xres eutran_vector.autn = autn eutran_vector.kasme = kasme logging.info("Auth success: %s", imsi) return aia except CryptoError as e: logging.error("Auth error for %s: %s", imsi, e) metrics.S6A_AUTH_FAILURE_TOTAL.labels( code=metrics.DIAMETER_AUTHENTICATION_REJECTED, ).inc() aia.error_code = metrics.DIAMETER_AUTHENTICATION_REJECTED return aia except SubscriberNotFoundError as e: logging.warning("Subscriber not found: %s", e) metrics.S6A_AUTH_FAILURE_TOTAL.labels( code=metrics.DIAMETER_ERROR_USER_UNKNOWN, ).inc() aia.error_code = metrics.DIAMETER_ERROR_USER_UNKNOWN return aia except ServiceNotActive as e: logging.error("Service not active for %s: %s", imsi, e) metrics.M5G_AUTH_FAILURE_TOTAL.labels( code=metrics.DIAMETER_ERROR_UNAUTHORIZED_SERVICE, ).inc() aia.error_code = metrics.DIAMETER_ERROR_UNAUTHORIZED_SERVICE return aia finally: print_grpc(aia, self._print_grpc_payload, "AIA:")
def AddSubscriber(self, request, context): """ Adds a subscriber to the store """ print_grpc(request, self._print_grpc_payload, "Add Subscriber Request:") sid = SIDUtils.to_str(request.sid) logging.debug("Add subscriber rpc for sid: %s", sid) try: self._store.add_subscriber(request) except DuplicateSubscriberError: context.set_details("Duplicate subscriber: %s" % sid) context.set_code(grpc.StatusCode.ALREADY_EXISTS)
def M5GDecryptImsiSUCIRegistration(self, request, context): """ M5GDecryptImsiSUCIRegistration """ print_grpc( request, self._print_grpc_payload, "M5GDecryptImsiSUCIRegistration Request:", ) aia = subscriberdb_pb2.M5GSUCIRegistrationAnswer() try: suciprofile = self.suciprofile_db.get(request.ue_pubkey_identifier) if suciprofile is None: set_grpc_err( context, StatusCode.NOT_FOUND, f"identifier {request.ue_pubkey_identifier} not found", ) return aia if suciprofile.protection_scheme == 0: profile = 'A' elif suciprofile.protection_scheme == 1: profile = 'B' home_network_info = ECIES_HN( suciprofile.home_net_private_key, profile, ) msin_recv = home_network_info.unprotect( request.ue_pubkey, request.ue_ciphertext, request.ue_encrypted_mac, ) aia.ue_msin_recv = msin_recv[:10] logging.info("Deconcealed IMSI: %s", aia.ue_msin_recv) return aia except SuciProfileNotFoundError as e: logging.warning("Suciprofile not found: %s", e) return aia finally: print_grpc( aia, self._print_grpc_payload, "M5GDecryptImsiSUCIRegistration Response:", )
async def _resync(self): states_to_sync = [] for redis_dict in self._redis_dicts: for key in redis_dict: version = redis_dict.get_version(key) device_id = make_scoped_device_id(key, redis_dict.state_scope) state_id = StateID( type=redis_dict.redis_type, deviceID=device_id, ) id_and_version = IDAndVersion(id=state_id, version=version) states_to_sync.append(id_and_version) if len(states_to_sync) == 0: logging.debug("Not re-syncing state. No local state found.") return state_client = self._grpc_client_manager.get_client() request = SyncStatesRequest(states=states_to_sync) print_grpc( request, self._print_grpc_payload, "Sending resync state request", ) response = await grpc_async_wrapper( state_client.SyncStates.future( request, DEFAULT_GRPC_TIMEOUT, ), self._loop, ) print_grpc( response, self._print_grpc_payload, "Received resync state request", ) unsynced_states = set() for id_and_version in response.unsyncedStates: unsynced_states.add(( id_and_version.id.type, id_and_version.id.deviceID, )) # Update in-memory map to add already synced states for state in request.states: in_mem_key = make_mem_key(state.id.deviceID, state.id.type) if (state.id.type, state.id.deviceID) not in unsynced_states: self._state_versions[in_mem_key] = state.version self._has_resync_completed = True logging.info("Successfully resynced state with Orchestrator!")
def ListSubscribers(self, request, context): # pylint:disable=unused-argument """ Return a list of subscribers from the store """ print_grpc( request, self._print_grpc_payload, "List Subscribers Request:", ) sids = self._store.list_subscribers() sid_msgs = [SIDUtils.to_pb(sid) for sid in sids] response = subscriberdb_pb2.SubscriberIDSet(sids=sid_msgs) print_grpc( response, self._print_grpc_payload, "List Subscribers Response:", ) return response
def UpdateSubscriber(self, request, context): """ Update the subscription data """ try: print_grpc( request, self._print_grpc_payload, "Update Subscriber Request", ) except Exception as e: # pylint: disable=broad-except logging.debug("Exception while trying to log GRPC: %s", e) sid = SIDUtils.to_str(request.data.sid) try: with self._store.edit_subscriber(sid) as subs: request.mask.MergeMessage( request.data, subs, replace_message_field=True, ) except SubscriberNotFoundError: context.set_details("Subscriber not found: %s" % sid) context.set_code(grpc.StatusCode.NOT_FOUND)
def GetStatus(self, _=None, context=None) -> ServiceStatus: """ Get eNodeB status Note: input variable defaults used so this can be either called locally or as an RPC. """ print_grpc( Void(), self._print_grpc_payload, "GetStatus Request:", ) status = dict(get_service_status(self.state_machine_manager)) status_message = ServiceStatus() status_message.meta.update(status) print_grpc( status_message, self._print_grpc_payload, "GetStatus Response:", ) return status_message
def GetEnodebStatus( self, request: EnodebIdentity, _context=None, ) -> SingleEnodebStatus: print_grpc( request, self._print_grpc_payload, "GetEnodebStatus Request:", ) response = get_single_enb_status( request.device_serial, self.state_machine_manager, ) print_grpc( response, self._print_grpc_payload, "GetEnodebStatus Response:", ) return response
def GetSubscriberData(self, request, context): """ Return the subscription data for the subscriber """ print_grpc( request, self._print_grpc_payload, "Get Subscriber Data Request:", ) sid = SIDUtils.to_str(request) try: response = self._store.get_subscriber_data(sid) except SubscriberNotFoundError: context.set_details("Subscriber not found: %s" % sid) context.set_code(grpc.StatusCode.NOT_FOUND) response = subscriberdb_pb2.SubscriberData() print_grpc( response, self._print_grpc_payload, "Get Subscriber Data Response:", ) return response
def UpdateLocation(self, request, context): print_grpc(request, self._print_grpc_payload, "ULR:") imsi = request.user_name ula = s6a_proxy_pb2.UpdateLocationAnswer() try: profile = self.lte_processor.get_sub_profile(imsi) except SubscriberNotFoundError as e: ula.error_code = s6a_proxy_pb2.USER_UNKNOWN logging.warning('Subscriber not found for ULR: %s', e) print_grpc(ula, self._print_grpc_payload, "ULA:") return ula try: sub_data = self.lte_processor.get_sub_data(imsi) except SubscriberNotFoundError as e: ula.error_code = s6a_proxy_pb2.USER_UNKNOWN logging.warning("Subscriber not found for ULR: %s", e) print_grpc(ula, self._print_grpc_payload, "ULA:") return ula ula.error_code = s6a_proxy_pb2.SUCCESS ula.default_context_id = 0 ula.total_ambr.max_bandwidth_ul = profile.max_ul_bit_rate ula.total_ambr.max_bandwidth_dl = profile.max_dl_bit_rate ula.all_apns_included = 0 ula.msisdn = self.encode_msisdn(sub_data.non_3gpp.msisdn) context_id = 0 for apn in sub_data.non_3gpp.apn_config: sec_apn = ula.apn.add() sec_apn.context_id = context_id context_id += 1 sec_apn.service_selection = apn.service_selection sec_apn.qos_profile.class_id = apn.qos_profile.class_id sec_apn.qos_profile.priority_level = apn.qos_profile.priority_level sec_apn.qos_profile.preemption_capability = ( apn.qos_profile.preemption_capability) sec_apn.qos_profile.preemption_vulnerability = ( apn.qos_profile.preemption_vulnerability) sec_apn.ambr.max_bandwidth_ul = apn.ambr.max_bandwidth_ul sec_apn.ambr.max_bandwidth_dl = apn.ambr.max_bandwidth_dl sec_apn.ambr.unit = (s6a_proxy_pb2.UpdateLocationAnswer. AggregatedMaximumBitrate.BitrateUnitsAMBR.BPS) sec_apn.pdn = ( apn.pdn if apn.pdn else s6a_proxy_pb2.UpdateLocationAnswer.APNConfiguration.IPV4) print_grpc(ula, self._print_grpc_payload, "ULA:") return ula
def GetParameter( self, request: GetParameterRequest, context: Any, ) -> GetParameterResponse: """ Sends a GetParameterValues message. Used for testing only. Different data models will have different names for the same parameter. Whatever name that the data model uses, we call the 'parameter path', eg. "Device.DeviceInfo.X_BAICELLS_COM_GPS_Status" We denote 'ParameterName' to be a standard string name for equivalent parameters between different data models """ print_grpc( request, self._print_grpc_payload, "GetParameter Request:", ) # Get the parameter value information parameter_path = request.parameter_name handler = self._get_handler(request.device_serial) data_model = handler.data_model param_name = data_model.get_parameter_name_from_path(parameter_path) param_value = str(handler.get_parameter(param_name)) # And now construct the response to the rpc request get_parameter_values_response = GetParameterResponse() get_parameter_values_response.parameters.add( name=parameter_path, value=param_value, ) print_grpc( get_parameter_values_response, self._print_grpc_payload, "GetParameter Response:", ) return get_parameter_values_response
def SetParameter(self, request: SetParameterRequest, context: Any) -> None: """ Sends a SetParameterValues message. Used for testing only. Different data models will have different names for the same parameter. Whatever name that the data model uses, we call the 'parameter path', eg. "Device.DeviceInfo.X_BAICELLS_COM_GPS_Status" We denote 'ParameterName' to be a standard string name for equivalent parameters between different data models """ print_grpc( request, self._print_grpc_payload, "SetParameter Request:", ) # Parse the request if request.HasField('value_int'): value = (request.value_int, 'int') elif request.HasField('value_bool'): value = (request.value_bool, 'boolean') elif request.HasField('value_string'): value = (request.value_string, 'string') else: context.set_details( 'SetParameter: Unsupported type %d', request.type, ) context.set_code(grpc.StatusCode.INVALID_ARGUMENT) return # Update the handler so it will set the parameter value parameter_path = request.parameter_name handler = self._get_handler(request.device_serial) data_model = handler.data_model param_name = data_model.get_parameter_name_from_path(parameter_path) handler.set_parameter_asap(param_name, value)