Beispiel #1
0
 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)
Beispiel #2
0
 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)
Beispiel #3
0
 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)
Beispiel #4
0
 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)
Beispiel #5
0
 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
Beispiel #6
0
 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)
Beispiel #7
0
 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()
Beispiel #8
0
 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)
Beispiel #9
0
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)
Beispiel #10
0
 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)
Beispiel #11
0
    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'
Beispiel #12
0
    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)
Beispiel #13
0
    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)
Beispiel #14
0
    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
Beispiel #15
0
 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()
Beispiel #16
0
 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)
Beispiel #17
0
    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('')
Beispiel #18
0
    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
Beispiel #19
0
    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)
Beispiel #20
0
    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
Beispiel #21
0
    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'
Beispiel #22
0
    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)
Beispiel #23
0
    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
Beispiel #24
0
    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
Beispiel #25
0
    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
Beispiel #26
0
    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,
        )
Beispiel #27
0
    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, )
Beispiel #28
0
 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()
Beispiel #29
0
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)
Beispiel #30
0
    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