def DeleteSubscriber(self, request, context): """ Deletes a subscriber from the store """ try: self._print_grpc(request) except Exception as e: # pylint: disable=broad-except logging.debug("Exception while trying to log GRPC: %s", e) sid = SIDUtils.to_str(request) logging.debug("Delete subscriber rpc for sid: %s", sid) self._store.delete_subscriber(sid)
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)
def ListSubscribers(self, request, context): # pylint:disable=unused-argument """ Returns a list of subscribers from the store """ try: self._print_grpc(request) except Exception as e: # pylint: disable=broad-except logging.debug("Exception while trying to log GRPC: %s", e) sids = self._store.list_subscribers() sid_msgs = [SIDUtils.to_pb(sid) for sid in sids] return subscriberdb_pb2.SubscriberIDSet(sids=sid_msgs)
def ReleaseIPAddress(self, request, context): """ Release an allocated IP address """ if request.ip.version == IPAddress.IPV4: try: ip = ipaddress.ip_address(request.ip.address) composite_sid = SIDUtils.to_str(request.sid) if request.apn: composite_sid = composite_sid + "." + request.apn self._ipv4_allocator.release_ip_address(composite_sid, ip) logging.info("Released IPv4 %s for sid %s" % (ip, SIDUtils.to_str(request.sid))) except IPNotInUseError: context.set_details('IP %s not in use' % ip) context.set_code(grpc.StatusCode.NOT_FOUND) except MappingNotFoundError: context.set_details('(SID, IP) map not found: (%s, %s)' % (SIDUtils.to_str(request.sid), ip)) context.set_code(grpc.StatusCode.NOT_FOUND) else: self._unimplemented_ip_version_error(context)
def get_sub_data(self, imsi): """ Returns the complete subscriber profile for subscriber. Args: imsi: IMSI string Returns: SubscriberData proto struct """ sid = SIDUtils.to_str(SubscriberID(id=imsi, type=SubscriberID.IMSI)) sub_data = self._store.get_subscriber_data(sid) return sub_data
def UpdateSubscriber(self, request, context): """ Updates the subscription data """ sid = SIDUtils.to_str(request.data.sid) try: with self._store.edit_subscriber(sid) as subs: request.mask.MergeMessage(request.data, subs) except SubscriberNotFoundError: context.set_details('Subscriber not found: %s' % sid) context.set_code(grpc.StatusCode.NOT_FOUND)
def GetSubscriberData(self, request, context): """ Returns the subscription data for the subscriber """ sid = SIDUtils.to_str(request) try: return self._store.get_subscriber_data(sid) except SubscriberNotFoundError: context.set_details('Subscriber not found: %s' % sid) context.set_code(grpc.StatusCode.NOT_FOUND) return subscriberdb_pb2.SubscriberData()
def AddSubscriber(self, request, context): """ Adds a subscriber to the store """ 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 deactivate_gy_flows(client, args): policies = [ VersionedPolicyID(rule_id=rule_id, version=1) for rule_id in args.rule_ids.split(',') if args.rule_ids ] request = DeactivateFlowsRequest( sid=SIDUtils.to_pb(args.imsi), ip_addr=args.ipv4, policies=policies, request_origin=RequestOriginType(type=RequestOriginType.GY)) client.DeactivateFlows(request)
def delete_bearer(self, imsi, lbi, ebi): """ Sends a DeleteBearer Request to SPGW service """ print('Sending DeleteBearer request to spgw service') req = DeleteBearerRequest( sid=SIDUtils.to_pb(imsi), link_bearer_id=lbi, eps_bearer_ids=[ebi] ) self._stub.DeleteBearer(req)
def setUp(self): # Bind the rpc server to a free port thread_pool = futures.ThreadPoolExecutor(max_workers=10) self._rpc_server = grpc.server(thread_pool) port = self._rpc_server.add_insecure_port('0.0.0.0:0') store = MobilityStore(get_default_client(), False, 3980) store.dhcp_gw_info.read_default_gw() ip_allocator = IpAllocatorPool(store) ipv6_allocator = IPv6AllocatorPool(store, session_prefix_alloc_mode='RANDOM') self._allocator = IPAddressManager(ip_allocator, ipv6_allocator, store) # Add the servicer self._servicer = MobilityServiceRpcServicer(self._allocator, False) self._servicer.add_to_server(self._rpc_server) self._rpc_server.start() # Create a rpc stub channel = grpc.insecure_channel('0.0.0.0:{}'.format(port)) self._stub = MobilityServiceStub(channel) # variables shared across tests self._netaddr = '192.168.0.0' self._prefix_len = 28 ip_bytes = bytes(map(int, self._netaddr.split('.'))) self._block_msg = IPBlock(version=IPBlock.IPV4, net_address=ip_bytes, prefix_len=self._prefix_len) self._ipv6_block = ipaddress.ip_network('fdee:5:6c::/48') self._ipv6_netaddr = self._ipv6_block.network_address.packed self._ipv6_block_msg = IPBlock(version=IPBlock.IPV6, net_address=self._ipv6_netaddr, prefix_len=self._ipv6_block.prefixlen) self._block = ipaddress.ip_network("%s/%s" % (self._netaddr, self._prefix_len)) self._sid0 = SIDUtils.to_pb('IMSI0') self._sid1 = SIDUtils.to_pb('IMSI1') self._sid2 = SIDUtils.to_pb('IMSI2') self._apn0 = 'Internet' self._apn1 = 'IMS'
def add_subscriber(self, subscriber_data): """ Method that adds the subscriber. """ sid = SIDUtils.to_str(subscriber_data.sid) with self._lock: if sid in self._cache: raise DuplicateSubscriberError(sid) self._persistent_store.add_subscriber(subscriber_data) self._cache_put(sid, subscriber_data) self._on_ready.add_subscriber(subscriber_data)
def GetSubscriberIDFromIP(self, ip_addr, context): sent_ip = ipaddress.ip_address(ip_addr.address) sid = self._ipv4_allocator.get_sid_for_ip(sent_ip) if sid is None: context.set_details('IP address %s not found' % str(sent_ip)) context.set_code(grpc.StatusCode.NOT_FOUND) return SubscriberID() else: #handle composite key case sid, *rest = sid.partition('.') return SIDUtils.to_pb(sid)
def AllocateIPAddress(self, request, context): """ Allocate an IP address from the free IP pool """ logging.debug("Received AllocateIPAddress") self._print_grpc(request) composite_sid = SIDUtils.to_str(request.sid) if request.apn: composite_sid = composite_sid + "." + request.apn if request.version == AllocateIPRequest.IPV4: resp = self._get_allocate_ip_response( composite_sid + ",ipv4", IPAddress.IPV4, context, request, ) elif request.version == AllocateIPRequest.IPV6: resp = self._get_allocate_ip_response( composite_sid + ",ipv6", IPAddress.IPV6, context, request, ) elif request.version == AllocateIPRequest.IPV4V6: ipv4_response = self._get_allocate_ip_response( composite_sid + ",ipv4", IPAddress.IPV4, context, request, ) ipv6_response = self._get_allocate_ip_response( composite_sid + ",ipv6", IPAddress.IPV6, context, request, ) try: ipv4_address = ipv4_response.ip_list[0] ipv6_address = ipv6_response.ip_list[0] except IndexError: logging.warning( "IPv4/IPv6 IP address allocation not successful") resp = AllocateIPAddressResponse() else: resp = AllocateIPAddressResponse( ip_list=[ipv4_address, ipv6_address], vlan=ipv4_response.vlan, ) else: resp = AllocateIPAddressResponse() self._print_grpc(resp) return resp
def _get_allocate_ip_response( self, composite_sid, version, context, request, ): try: ip, vlan = self._ip_address_man.alloc_ip_address( composite_sid, version, ) logging.info( "Allocated IP %s for sid %s for apn %s" % (ip, SIDUtils.to_str(request.sid), request.apn), ) ip_addr = IPAddress(address=ip.packed, version=version) return AllocateIPAddressResponse( ip_list=[ip_addr], vlan=str(vlan), ) except NoAvailableIPError: context.set_details('No free IP available') context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED) except DuplicatedIPAllocationError: context.set_details( 'IP has been allocated for this subscriber', ) context.set_code(grpc.StatusCode.ALREADY_EXISTS) except DuplicateIPAssignmentError: context.set_details( 'IP has been allocated for other subscriber', ) context.set_code(grpc.StatusCode.ALREADY_EXISTS) except MaxCalculationError: context.set_details( 'Reached maximum IPv6 calculation tries', ) context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED) except SubscriberDBConnectionError: context.set_details( 'Could not connect to SubscriberDB', ) context.set_code(grpc.StatusCode.FAILED_PRECONDITION) except SubscriberDBStaticIPValueError: context.set_details( 'Could not parse static IP response from SubscriberDB', ) context.set_code(grpc.StatusCode.FAILED_PRECONDITION) except SubscriberDBMultiAPNValueError: context.set_details( 'Could not parse MultiAPN IP response from SubscriberDB', ) context.set_code(grpc.StatusCode.FAILED_PRECONDITION) return AllocateIPAddressResponse()
def add_srsue(self): # Add for srsUE, add the hardcoded SubscriberData into the store when receiving request imsi = 'IMSI' + SRSUE_IMSI lte = LTESubscription() lte.state = LTESubscription.ACTIVE lte.auth_key = bytes.fromhex(SRSUE_KEY) #lte.auth_opc = bytes.fromhex(SRSUE_OPC) state = SubscriberState() state.lte_auth_next_seq = 1 sub_data = SubscriberData(sid=SIDUtils.to_pb(imsi), lte=lte, state=state) self.add_subscriber(sub_data)
def test_str_conversion(self): """ Tests the string conversion utils """ sid = SubscriberID(id='12345', type=SubscriberID.IMSI) self.assertEqual(SIDUtils.to_str(sid), 'IMSI12345') self.assertEqual(SIDUtils.to_pb('IMSI12345'), sid) # By default the id type is IMSI sid = SubscriberID(id='12345') self.assertEqual(SIDUtils.to_str(sid), 'IMSI12345') self.assertEqual(SIDUtils.to_pb('IMSI12345'), sid) # Raise ValueError if invalid strings are given with self.assertRaises(ValueError): SIDUtils.to_pb('IMS') with self.assertRaises(ValueError): SIDUtils.to_pb('IMSI12345a') with self.assertRaises(ValueError): SIDUtils.to_pb('')
def CreateDelQERinPDR( qos_enforce_rule: QoSEnforceRuleEntry, ue_ip_addr: str, ) -> DeactivateFlowsRequest: qos_enforce_rule = DeactivateFlowsRequest( sid=SIDUtils.to_pb(qos_enforce_rule.imsi), ip_addr=ue_ip_addr, policies=[VersionedPolicyID(rule_id=qos_enforce_rule.rule_id)], request_origin=RequestOriginType(type=RequestOriginType.N4), ) return qos_enforce_rule
def add_sub(cls, sid: str, apn: str, ip: str, vlan: str = None, gw_ip=None, gw_mac=None): sub_db_sid = SIDUtils.to_pb(sid) lte = LTESubscription() lte.state = LTESubscription.ACTIVE state = SubscriberState() state.lte_auth_next_seq = 1 non_3gpp = Non3GPPUserProfile() subs_data = SubscriberData(sid=sub_db_sid, lte=lte, state=state, non_3gpp=non_3gpp) cls.subs[str(sub_db_sid)] = subs_data cls.add_sub_ip(sid, apn, ip, vlan, gw_ip, gw_mac)
def GetSubscriberIPTable(self, void, context): """ Get the full subscriber table """ logging.debug("Listing subscriber IP table") resp = SubscriberIPTable() sid_ip_pairs = self._ipv4_allocator.get_sid_ip_table() for subscriber_id, ip in sid_ip_pairs: sid = SIDUtils.to_pb(subscriber_id) version = IPAddress.IPV4 if ip.version == 4 else IPAddress.IPV6 ip_msg = IPAddress(version=version, address=ip.packed) resp.entries.add(sid=sid, ip=ip_msg) return resp
def setUp(self): # Bind the rpc server to a free port thread_pool = futures.ThreadPoolExecutor(max_workers=10) self._rpc_server = grpc.server(thread_pool) port = self._rpc_server.add_insecure_port('0.0.0.0:0') # Create a mock "mconfig" for the servicer to use mconfig = unittest.mock.Mock() mconfig.ip_block = None mconfig.ip_allocator_type = MobilityD.IP_POOL # Add the servicer config = { 'persist_to_redis': False, 'redis_port': None, 'allocator_type': "ip_pool" } self._servicer = MobilityServiceRpcServicer(mconfig, config) self._servicer.add_to_server(self._rpc_server) self._rpc_server.start() # Create a rpc stub channel = grpc.insecure_channel('0.0.0.0:{}'.format(port)) self._stub = MobilityServiceStub(channel) # variables shared across tests self._netaddr = '192.168.0.0' self._prefix_len = 28 ip_bytes = bytes(map(int, self._netaddr.split('.'))) self._block_msg = IPBlock(version=IPBlock.IPV4, net_address=ip_bytes, prefix_len=self._prefix_len) self._block = ipaddress.ip_network("%s/%s" % (self._netaddr, self._prefix_len)) self._sid0 = SIDUtils.to_pb('IMSI0') self._sid1 = SIDUtils.to_pb('IMSI1') self._sid2 = SIDUtils.to_pb('IMSI2') self._apn0 = 'Internet' self._apn1 = 'IMS'
def test_upsert_subscriber(self): """ Test if subscriber upsertion triggers ready """ self.assertEqual(self._store._on_ready.event.is_set(), False) self._store.upsert_subscriber( SubscriberData(sid=SIDUtils.to_pb('IMSI1111')), ) async def defer(): await self._store.on_ready() self.loop.run_until_complete(defer()) self.assertEqual(self._store._on_ready.event.is_set(), True)
def AllocateIPAddress(self, request, context): """ Allocate an IP address from the free IP pool """ resp = IPAddress() if request.version == AllocateIPRequest.IPV4: try: composite_sid = SIDUtils.to_str(request.sid) if request.apn: composite_sid = composite_sid + "." + request.apn ip = self._ipv4_allocator.alloc_ip_address(composite_sid) logging.info("Allocated IPv4 %s for sid %s for apn %s" % (ip, SIDUtils.to_str(request.sid), request.apn)) resp.version = IPAddress.IPV4 resp.address = ip.packed except NoAvailableIPError: context.set_details('No free IPv4 IP available') context.set_code(grpc.StatusCode.RESOURCE_EXHAUSTED) except DuplicatedIPAllocationError: context.set_details('IP has been allocated for this subscriber') context.set_code(grpc.StatusCode.ALREADY_EXISTS) else: self._unimplemented_ip_version_error(context) return resp
def get_next_lte_auth_seq(self, imsi): """ Returns the sequence number for the next auth operation. """ sid = SIDUtils.to_str(SubscriberID(id=imsi, type=SubscriberID.IMSI)) # Increment the sequence number. # The 3GPP TS 33.102 spec allows wrapping around the maximum value. # The re-synchronization mechanism would be used to sync the counter # between USIM and HSS when it happens. with self._store.edit_subscriber(sid) as subs: seq = subs.state.lte_auth_next_seq subs.state.lte_auth_next_seq += 1 return seq
def GetSubscriberIPTable(self, void, context): """ Get the full subscriber table """ logging.debug("Listing subscriber IP table") resp = SubscriberIPTable() csid_ip_pairs = self._ipv4_allocator.get_sid_ip_table() for composite_sid, ip in csid_ip_pairs: #handle composite sid to sid and apn mapping sid, _, apn = composite_sid.partition('.') sid_pb = SIDUtils.to_pb(sid) version = IPAddress.IPV4 if ip.version == 4 else IPAddress.IPV6 ip_msg = IPAddress(version=version, address=ip.packed) resp.entries.add(sid=sid_pb, ip=ip_msg, apn=apn) return resp
def ListSubscribers(self, request, context): # noqa: N802 """ List subscribers is a mock to trigger various test cases Args: request: ListSubscribersRequest context: request context Raises: RpcError: If page size is 1 Returns: ListSubscribersResponse """ # Add in logic to allow error handling testing flat_digest = Digest(md5_base64_digest="") per_sub_digests = [] if request.page_size == 1: raise grpc.RpcError("Test Exception") if request.page_token == "": next_page_token = "aaa" # noqa: S105 subscribers = [ SubscriberData(sid=SubscriberID(id="IMSI111")), SubscriberData(sid=SubscriberID(id="IMSI222")), ] flat_digest = Digest(md5_base64_digest="flat_digest_apple") per_sub_digests = [ SubscriberDigestWithID( sid=SIDUtils.to_pb("IMSI11111"), digest=Digest(md5_base64_digest="per_sub_digests_apple"), ), ] elif request.page_token == "aaa": next_page_token = "bbb" # noqa: S105 subscribers = [ SubscriberData(sid=SubscriberID(id="IMSI333")), SubscriberData(sid=SubscriberID(id="IMSI444")), ] else: next_page_token = "" # noqa: S105 subscribers = [ SubscriberData(sid=SubscriberID(id="IMSI555")), SubscriberData(sid=SubscriberID(id="IMSI666")), ] return ListSubscribersResponse( subscribers=subscribers, next_page_token=next_page_token, flat_digest=flat_digest, per_sub_digests=per_sub_digests, )
def resync_lte_auth_seq(self, imsi, rand, auts): """ Validates a re-synchronization request and computes the SEQ from the AUTS sent by U-SIM """ sid = SIDUtils.to_str(SubscriberID(id=imsi, type=SubscriberID.IMSI)) subs = self._store.get_subscriber_data(sid) if subs.lte.state != LTESubscription.ACTIVE: raise CryptoError("LTE service not active for %s" % sid) if subs.lte.auth_algo != LTESubscription.MILENAGE: raise CryptoError( "Unknown crypto (%s) for %s" % (subs.lte.auth_algo, sid), ) if len(subs.lte.auth_key) != 16: raise CryptoError("Subscriber key not valid for %s" % sid) if len(subs.lte.auth_opc) == 0: opc = Milenage.generate_opc(subs.lte.auth_key, self._op) elif len(subs.lte.auth_opc) != 16: raise CryptoError("Subscriber OPc is invalid length for %s" % sid) else: opc = subs.lte.auth_opc dummy_amf = b'\x00\x00' # Use dummy AMF for re-synchronization milenage = Milenage(dummy_amf) sqn_ms, mac_s = \ milenage.generate_resync(auts, subs.lte.auth_key, opc, rand) if mac_s != auts[6:]: raise CryptoError("Invalid resync authentication code") seq_ms = self.sqn_to_seq(sqn_ms) # current_seq_number was the seq number the network sent # to the mobile station as part of the original auth request. current_seq_number = subs.state.lte_auth_next_seq - 1 if seq_ms >= current_seq_number: self.set_next_lte_auth_seq(imsi, seq_ms + 1) else: seq_delta = current_seq_number - seq_ms if seq_delta > (2**28): self.set_next_lte_auth_seq(imsi, seq_ms + 1) else: # This shouldn't have happened raise CryptoError( "Re-sync delta in range but UE rejected " "auth: %d" % seq_delta, )
def GetSubscriberData(self, request, context): """ Returns the subscription data for the subscriber """ try: self._print_grpc(request) except Exception as e: # pylint: disable=broad-except logging.debug("Exception while trying to log GRPC: %s", e) sid = SIDUtils.to_str(request) try: return self._store.get_subscriber_data(sid) except SubscriberNotFoundError: context.set_details("Subscriber not found: %s" % sid) context.set_code(grpc.StatusCode.NOT_FOUND) return subscriberdb_pb2.SubscriberData()
def activate_dynamic_rule(client, args): request = ActivateFlowsRequest( sid=SIDUtils.to_pb(args.imsi), dynamic_rules=[PolicyRule( id=args.rule_id, priority=args.priority, hard_timeout=args.hard_timeout, flow_list=[ FlowDescription(match=FlowMatch( ipv4_dst=args.ipv4_dst, direction=FlowMatch.UPLINK)), FlowDescription(match=FlowMatch( ipv4_src=args.ipv4_dst, direction=FlowMatch.DOWNLINK)), ], )]) client.ActivateFlows(request)
def GetIPForSubscriber(self, request, context): logging.debug("Received GetIPForSubscriber") self._print_grpc(request) composite_sid = SIDUtils.to_str(request.sid) if request.apn: composite_sid = composite_sid + "." + request.apn if request.version == IPAddress.IPV4: composite_sid += ",ipv4" elif request.version == IPAddress.IPV6: composite_sid += ",ipv6" ip = self._ip_address_man.get_ip_for_sid(composite_sid) if ip is None: context.set_details( 'SID %s not found' % SIDUtils.to_str(request.sid), ) context.set_code(grpc.StatusCode.NOT_FOUND) resp = IPAddress() else: version = IPAddress.IPV4 if ip.version == 4 else IPAddress.IPV6 resp = IPAddress(version=version, address=ip.packed) self._print_grpc(resp) return resp