Exemple #1
0
    def test_empty_value(self):
        """
        Tests we can initialize an AVP with no value
        """
        avp_val = avp.AVP(0)
        self.assertEqual(avp_val.value, None)
        self.assertEqual(avp_val.payload, None)

        # We can then set its value
        avp_val.value = b''
        self.assertEqual(avp_val.value, b'')
        self.assertEqual(avp_val.payload, b'')

        # And unset it again
        avp_val.value = None
        self.assertEqual(avp_val.value, None)
        self.assertEqual(avp_val.payload, None)
 def _auth_req(user_name, visited_plmn_id, num_request_vectors,
               immediate_response_preferred, resync_info):
     msg = message.Message()
     msg.header.application_id = s6a.S6AApplication.APP_ID
     msg.header.command_code = s6a.S6AApplicationCommands.AUTHENTICATION_INFORMATION
     msg.header.request = True
     msg.append_avp(
         avp.AVP('Session-Id',
                 'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a'))
     msg.append_avp(avp.AVP('Auth-Session-State', 1))
     msg.append_avp(avp.AVP('User-Name', user_name))
     msg.append_avp(avp.AVP('Visited-PLMN-Id', visited_plmn_id))
     msg.append_avp(
         avp.AVP('Requested-EUTRAN-Authentication-Info', [
             avp.AVP('Number-Of-Requested-Vectors', num_request_vectors),
             avp.AVP('Immediate-Response-Preferred',
                     1 if immediate_response_preferred else 0),
             avp.AVP('Re-Synchronization-Info', resync_info),
         ]))
     return msg
 def _update_location_req(user_name, visited_plmn_id, ulr_flags):
     msg = message.Message()
     msg.header.application_id = s6a.S6AApplication.APP_ID
     msg.header.command_code = s6a.S6AApplicationCommands.UPDATE_LOCATION
     msg.header.request = True
     msg.append_avp(
         avp.AVP('Session-Id',
                 'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a'))
     msg.append_avp(avp.AVP('Auth-Session-State', 1))
     msg.append_avp(avp.AVP('User-Name', user_name))
     msg.append_avp(avp.AVP('Visited-PLMN-Id', visited_plmn_id))
     msg.append_avp(avp.AVP('ULR-Flags', ulr_flags))
     msg.append_avp(avp.AVP('RAT-Type', 1004))
     return msg
    def test_resync(self):
        """
        Test that we can respond to auth requests with an auth
        vector
        """
        msg = message.Message()
        msg.header.application_id = s6a.S6AApplication.APP_ID
        msg.header.command_code = s6a.S6AApplicationCommands.AUTHENTICATION_INFORMATION
        msg.header.request = True
        msg.append_avp(
            avp.AVP(
                'Session-Id',
                'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a',
            ), )
        msg.append_avp(avp.AVP('Auth-Session-State', 1))
        msg.append_avp(avp.AVP('User-Name', '1'))
        msg.append_avp(avp.AVP('Visited-PLMN-Id', b'(Y'))
        msg.append_avp(
            avp.AVP(
                'Requested-EUTRAN-Authentication-Info',
                [
                    avp.AVP('Number-Of-Requested-Vectors', 1),
                    avp.AVP('Immediate-Response-Preferred', 0),
                    avp.AVP('Re-Synchronization-Info', 30 * b'\x00'),
                ],
            ), )
        # Encode request message into buffer
        req_buf = bytearray(msg.length)
        msg.encode(req_buf, 0)

        processor = self._server._s6a_manager.lte_processor
        with unittest.mock.patch.object(processor, 'resync_lte_auth_seq'):
            self._server.data_received(req_buf)
            processor.resync_lte_auth_seq.assert_called_once_with(
                '1',
                16 * b'\x00',
                14 * b'\x00',
            )
    def test_auth_answer_success(self):
        """
        Tests that we convert gRPC AuthenticationInformation success response into Diameter AIA
        """
        state_id = 1
        user_name = '1'
        visited_plmn_id = b'(Y'
        num_request_vectors = 2
        immediate_response_preferred = True
        resync_info = b'123456789'
        req = self._auth_req(user_name, visited_plmn_id, num_request_vectors,
                             immediate_response_preferred, resync_info)

        # response
        rand = b'rand'
        xres = b'xres'
        autn = b'autn'
        kasme = b'kasme'
        auth_info = avp.AVP('Authentication-Info', [
            avp.AVP('E-UTRAN-Vector', [
                avp.AVP('RAND', rand),
                avp.AVP('XRES', xres),
                avp.AVP('AUTN', autn),
                avp.AVP('KASME', kasme)
            ])
        ] * num_request_vectors)
        resp = self._server._s6a_manager._gen_response(
            state_id, req, avp.ResultCode.DIAMETER_SUCCESS, [auth_info])
        resp_buf = bytearray(resp.length)
        resp.encode(resp_buf, 0)

        result = AuthenticationInformationAnswer(
            error_code=0,
            eutran_vectors=[
                AuthenticationInformationAnswer.EUTRANVector(
                    rand=rand, xres=xres, autn=autn, kasme=kasme)
            ] * num_request_vectors)
        result_future = unittest.mock.Mock()
        result_future.exception.side_effect = [None]
        result_future.result.side_effect = [result]

        self._server._s6a_manager._relay_auth_answer(state_id, req,
                                                     result_future, 0)
        self._writes.assert_called_once_with(resp_buf)
        self._writes.reset_mock()
Exemple #6
0
    def test_avp_length(self):
        """
        Tests we validate AVPs lengths are longer than minumum to decode
        and no longer than the maximum length encodable
        """
        # Avp that has no payload isn't encodable
        with self.assertRaises(CodecException):
            avp_val = avp.AVP(0)
            out_buf = bytearray(avp_val.length)
            avp_val.encode(out_buf, 0)

        # Avp shorter than header
        with self.assertRaises(CodecException):
            avp.decode(b'\x00' * (avp.HEADER_LEN - 1))

        # Too short with vendor bit set
        with self.assertRaises(CodecException):
            avp.decode(b'\x00\x00\x00\x00\x80\x00\x00\x00')

        # Max allowable length of payload
        avp_val = avp.UTF8StringAVP(
            1,
            'a' * (0x00FFFFFF - avp.HEADER_LEN),
        )
        out_buf = bytearray(avp_val.length)
        avp_val.encode(out_buf, 0)
        self._compare_avp(avp_val, out_buf)

        # Avp length out of range
        with self.assertRaises(CodecException):
            avp_val = avp.UTF8StringAVP(
                1,
                'a' * (0x00FFFFFF - avp.HEADER_LEN + 1),
            )
            out_buf = bytearray(avp_val.length)
            avp_val.encode(out_buf, 0)
Exemple #7
0
 def setUpClass(cls):
     cls.msg = message.Message()
     cls.msg.append_avp(avp.AVP('User-Name', 'hello'))
     cls.msg.append_avp(avp.AVP('User-Name', 'world'))
     cls.msg.append_avp(avp.AVP('Host-IP-Address', '127.0.0.1'))
    def test_location_update(self):
        """
        Test that we can respond to update location request with
        subscriber data
        """
        msg = message.Message()
        msg.header.application_id = s6a.S6AApplication.APP_ID
        msg.header.command_code = s6a.S6AApplicationCommands.UPDATE_LOCATION
        msg.header.request = True
        msg.append_avp(
            avp.AVP(
                'Session-Id',
                'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a',
            ), )
        msg.append_avp(avp.AVP('Auth-Session-State', 1))
        msg.append_avp(avp.AVP('User-Name', '208950000000001'))
        msg.append_avp(avp.AVP('Visited-PLMN-Id', b'(Y'))
        msg.append_avp(avp.AVP('RAT-Type', 1004))
        msg.append_avp(avp.AVP('ULR-Flags', 34))
        # Encode request message into buffer
        req_buf = bytearray(msg.length)
        msg.encode(req_buf, 0)

        msg = message.Message()
        msg.header.application_id = s6a.S6AApplication.APP_ID
        msg.header.command_code = s6a.S6AApplicationCommands.UPDATE_LOCATION
        msg.header.request = False
        msg.append_avp(
            avp.AVP(
                'Session-Id',
                'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a',
            ), )
        msg.append_avp(avp.AVP('ULA-Flags', 1))
        msg.append_avp(
            avp.AVP(
                'Subscription-Data',
                [
                    avp.AVP('MSISDN', b'333608050011'),
                    avp.AVP('Access-Restriction-Data', 47),
                    avp.AVP('Subscriber-Status', 0),
                    avp.AVP('Network-Access-Mode', 2),
                    avp.AVP(
                        'AMBR',
                        [
                            avp.AVP('Max-Requested-Bandwidth-UL', 10000),
                            avp.AVP('Max-Requested-Bandwidth-DL', 50000),
                        ],
                    ),
                    avp.AVP(
                        'APN-Configuration-Profile',
                        [
                            avp.AVP('Context-Identifier', 0),
                            avp.AVP(
                                'All-APN-Configurations-Included-Indicator',
                                0),
                            avp.AVP(
                                'APN-Configuration',
                                [
                                    avp.AVP('Context-Identifier', 0),
                                    avp.AVP('PDN-Type', 0),
                                    avp.AVP('Service-Selection', 'oai.ipv4'),
                                    avp.AVP(
                                        'EPS-Subscribed-QoS-Profile',
                                        [
                                            avp.AVP('QoS-Class-Identifier', 9),
                                            avp.AVP(
                                                'Allocation-Retention-Priority',
                                                [
                                                    avp.AVP(
                                                        'Priority-Level', 15),
                                                    avp.AVP(
                                                        'Pre-emption-Capability',
                                                        1),
                                                    avp.AVP(
                                                        'Pre-emption-Vulnerability',
                                                        0),
                                                ],
                                            ),
                                        ],
                                    ),
                                    avp.AVP(
                                        'AMBR',
                                        [
                                            avp.AVP(
                                                'Max-Requested-Bandwidth-UL',
                                                10000),
                                            avp.AVP(
                                                'Max-Requested-Bandwidth-DL',
                                                50000),
                                        ],
                                    ),
                                ],
                            ),
                        ],
                    ),
                ],
            ), )
        msg.append_avp(avp.AVP('Auth-Session-State', 1))

        # Host identifiers
        msg.append_avp(avp.AVP('Origin-Host', self._server.host))
        msg.append_avp(avp.AVP('Origin-Realm', self._server.realm))
        msg.append_avp(avp.AVP('Origin-State-Id', self._server.state_id))

        # Response result
        msg.append_avp(avp.AVP('Result-Code', avp.ResultCode.DIAMETER_SUCCESS))
        # Encode response into buffer
        resp_buf = bytearray(msg.length)
        msg.encode(resp_buf, 0)

        self._check_reply(req_buf, resp_buf)
    def test_auth_unknown_subscriber(self):
        """
        Test that we reject auth requests if the subscriber is unknown
        """
        msg = message.Message()
        msg.header.application_id = s6a.S6AApplication.APP_ID
        msg.header.command_code = s6a.S6AApplicationCommands.AUTHENTICATION_INFORMATION
        msg.header.request = True
        msg.append_avp(
            avp.AVP(
                'Session-Id',
                'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a',
            ), )
        msg.append_avp(avp.AVP('Auth-Session-State', 1))
        msg.append_avp(avp.AVP('User-Name', '3'))
        msg.append_avp(avp.AVP('Visited-PLMN-Id', b'(Y'))
        msg.append_avp(
            avp.AVP(
                'Requested-EUTRAN-Authentication-Info',
                [
                    avp.AVP('Number-Of-Requested-Vectors', 1),
                    avp.AVP('Immediate-Response-Preferred', 0),
                ],
            ), )
        # Encode request message into buffer
        req_buf = bytearray(msg.length)
        msg.encode(req_buf, 0)

        msg = message.Message()
        msg.header.application_id = s6a.S6AApplication.APP_ID
        msg.header.command_code = \
            s6a.S6AApplicationCommands.AUTHENTICATION_INFORMATION
        msg.header.request = False
        msg.append_avp(
            avp.AVP(
                'Session-Id',
                'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a',
            ), )
        msg.append_avp(avp.AVP('Auth-Session-State', 1))

        # Host identifiers
        msg.append_avp(avp.AVP('Origin-Host', self._server.host))
        msg.append_avp(avp.AVP('Origin-Realm', self._server.realm))
        msg.append_avp(avp.AVP('Origin-State-Id', self._server.state_id))

        # Response result
        msg.append_avp(
            avp.AVP(
                'Result-Code',
                avp.ResultCode.DIAMETER_ERROR_USER_UNKNOWN,
            ), )
        # Encode response into buffer
        resp_buf = bytearray(msg.length)
        msg.encode(resp_buf, 0)
        self._check_reply(req_buf, resp_buf)
    def test_auth_success(self):
        """
        Test that we can respond to auth requests with an auth
        vector
        """
        msg = message.Message()
        msg.header.application_id = s6a.S6AApplication.APP_ID
        msg.header.command_code = s6a.S6AApplicationCommands.AUTHENTICATION_INFORMATION
        msg.header.request = True
        msg.append_avp(
            avp.AVP(
                'Session-Id',
                'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a',
            ), )
        msg.append_avp(avp.AVP('Auth-Session-State', 1))
        msg.append_avp(avp.AVP('User-Name', '1'))
        msg.append_avp(avp.AVP('Visited-PLMN-Id', b'(Y'))
        msg.append_avp(
            avp.AVP(
                'Requested-EUTRAN-Authentication-Info',
                [
                    avp.AVP('Number-Of-Requested-Vectors', 1),
                    avp.AVP('Immediate-Response-Preferred', 0),
                ],
            ), )
        # Encode request message into buffer
        req_buf = bytearray(msg.length)
        msg.encode(req_buf, 0)

        msg = message.Message()
        msg.header.application_id = s6a.S6AApplication.APP_ID
        msg.header.command_code = \
            s6a.S6AApplicationCommands.AUTHENTICATION_INFORMATION
        msg.header.request = False
        msg.append_avp(
            avp.AVP(
                'Session-Id',
                'enb-Lenovo-Product.openair4G.eur;1475864727;1;apps6a',
            ), )
        msg.append_avp(
            avp.AVP(
                'Authentication-Info',
                [
                    avp.AVP(
                        'E-UTRAN-Vector',
                        [
                            avp.AVP(
                                'RAND',
                                b'\x00\x01\x02\x03\x04\x05'
                                b'\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f',
                            ),
                            avp.AVP('XRES',
                                    b'\x2d\xaf\x87\x3d\x73\xf3\x10\xc6'),
                            avp.AVP(
                                'AUTN',
                                b'\x6f\xbf\xa3\x80\x1f\x57\x80'
                                b'\x00\x7b\xde\x59\x88\x6e\x96\xe4\xfe',
                            ),
                            avp.AVP(
                                'KASME',
                                b'\x87\x48\xc1\xc0\xa2\x82'
                                b'\x6f\xa4\x05\xb1\xe2\x7e\xa1\x04\x43\x4a'
                                b'\xe5\x56\xc7\x65\xe8\xf0\x61\xeb\xdb\x8a'
                                b'\xe2\x86\xc4\x46\x16\xc2',
                            ),
                        ],
                    ),
                ],
            ), )
        msg.append_avp(avp.AVP('Auth-Session-State', 1))

        # Host identifiers
        msg.append_avp(avp.AVP('Origin-Host', self._server.host))
        msg.append_avp(avp.AVP('Origin-Realm', self._server.realm))
        msg.append_avp(avp.AVP('Origin-State-Id', self._server.state_id))

        # Response result
        msg.append_avp(avp.AVP('Result-Code', avp.ResultCode.DIAMETER_SUCCESS))
        # Encode response into buffer
        resp_buf = bytearray(msg.length)
        msg.encode(resp_buf, 0)

        self._check_reply(req_buf, resp_buf)
Exemple #11
0
    def _relay_auth_answer(self, state_id, msg, answer_future, retries_left):
        user_name = msg.find_avp(*avp.resolve('User-Name')).value
        err = answer_future.exception()
        if err and retries_left > 0:
            # TODO: retry only on network failure and not application failures
            logging.info(
                "Auth %s Error! [%s] %s, retrying...",
                user_name,
                err.code(),
                err.details(),
            )
            self._send_auth_with_retries(
                state_id,
                msg,
                retries_left - 1,
            )
            return
        elif err:
            logging.warning(
                "Auth %s Error! [%s] %s",
                user_name,
                err.code(),
                err.details(),
            )
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_UNABLE_TO_COMPLY,
            )
            S6A_AUTH_FAILURE_TOTAL.labels(
                code=avp.ResultCode.DIAMETER_UNABLE_TO_COMPLY, ).inc()
        else:
            answer = answer_future.result()
            error_code = answer.error_code
            if answer.error_code:
                result_info = avp.AVP(
                    'Experimental-Result',
                    [
                        avp.AVP('Vendor-Id', 10415),
                        avp.AVP('Experimental-Result-Code', error_code),
                    ],
                )
                resp = self._gen_response(
                    state_id,
                    msg,
                    error_code,
                    [result_info],
                )
                logging.warning(
                    "Auth S6a %s Error! [%s]",
                    user_name,
                    error_code,
                )
                S6A_AUTH_FAILURE_TOTAL.labels(code=error_code, ).inc()
            else:
                auth_info = avp.AVP(
                    'Authentication-Info',
                    [
                        avp.AVP(
                            'E-UTRAN-Vector',
                            [
                                avp.AVP('RAND', vector.rand),
                                avp.AVP('XRES', vector.xres),
                                avp.AVP('AUTN', vector.autn),
                                avp.AVP('KASME', vector.kasme),
                            ],
                        ) for vector in answer.eutran_vectors
                    ],
                )

                resp = self._gen_response(
                    state_id,
                    msg,
                    avp.ResultCode.DIAMETER_SUCCESS,
                    [auth_info],
                )
                S6A_AUTH_SUCCESS_TOTAL.inc()
        self.writer.send_msg(resp)
Exemple #12
0
    def test_location_update_resp(self):
        """
        Test that gRPC UpdateLocation success response triggers Diameter ULA success
        """
        state_id = 1
        user_name = '1'
        visited_plmn_id = b'(Y'
        ulr_flags = 1 << 2 | 1 << 5
        default_context_id = 0
        total_ambr = {'ul': 10000, 'dl': 50000}
        all_apns_included = True
        req = self._update_location_req(user_name, visited_plmn_id, ulr_flags)

        # Encode request message into buffer
        req_buf = bytearray(req.length)
        req.encode(req_buf, 0)

        apns = [{
            'context_id': i,
            'service_selection': 'apn.%d' % i,
            'qos_profile': {
                'class_id': i,
                'priority_level': i,
                'preemption_capability': True if i % 2 else False,
                'preemption_vulnerability': False if i % 2 else True,
            },
            'ambr': {
                'ul': 1000 * i,
                'dl': 2000 * i,
            },
        } for i in range(2)]

        resp_avps = [
            avp.AVP('ULA-Flags', 1),
            avp.AVP(
                'Subscription-Data',
                [
                    avp.AVP('MSISDN', b'333608050011'),
                    avp.AVP('Access-Restriction-Data', 47),
                    avp.AVP('Subscriber-Status', 0),
                    avp.AVP('Network-Access-Mode', 2),
                    avp.AVP(
                        'AMBR',
                        [
                            avp.AVP(
                                'Max-Requested-Bandwidth-UL',
                                total_ambr['ul'],
                            ),
                            avp.AVP(
                                'Max-Requested-Bandwidth-DL',
                                total_ambr['dl'],
                            ),
                        ],
                    ),
                    avp.AVP(
                        'APN-Configuration-Profile',
                        [
                            avp.AVP('Context-Identifier', default_context_id),
                            avp.AVP(
                                'All-APN-Configurations-Included-Indicator',
                                1 if all_apns_included else 0,
                            ),
                            *[
                                avp.AVP(
                                    'APN-Configuration',
                                    [
                                        avp.AVP(
                                            'Context-Identifier',
                                            apn['context_id'],
                                        ),
                                        avp.AVP('PDN-Type', 0),
                                        avp.AVP(
                                            'Service-Selection',
                                            apn['service_selection'],
                                        ),
                                        avp.AVP(
                                            'EPS-Subscribed-QoS-Profile',
                                            [
                                                avp.AVP(
                                                    'QoS-Class-Identifier',
                                                    apn['qos_profile']
                                                    ['class_id'],
                                                ),
                                                avp.AVP(
                                                    'Allocation-Retention-Priority',
                                                    [
                                                        avp.AVP(
                                                            'Priority-Level',
                                                            apn['qos_profile']
                                                            ['priority_level'],
                                                        ),
                                                        avp.AVP(
                                                            'Pre-emption-Capability',
                                                            apn['qos_profile']
                                                            ['preemption_capability'],
                                                        ),
                                                        avp.AVP(
                                                            'Pre-emption-Vulnerability',
                                                            apn['qos_profile']
                                                            ['preemption_vulnerability'],
                                                        ),
                                                    ],
                                                ),
                                            ],
                                        ),
                                        avp.AVP(
                                            'AMBR',
                                            [
                                                avp.AVP(
                                                    'Max-Requested-Bandwidth-UL',
                                                    apn['ambr']['ul'],
                                                ),
                                                avp.AVP(
                                                    'Max-Requested-Bandwidth-DL',
                                                    apn['ambr']['dl'],
                                                ),
                                            ],
                                        ),
                                    ],
                                ) for apn in apns
                            ],
                        ],
                    ),
                ],
            ),
        ]
        resp = self._server._s6a_manager._gen_response(
            state_id,
            req,
            avp.ResultCode.DIAMETER_SUCCESS,
            resp_avps,
        )
        resp_buf = bytearray(resp.length)
        resp.encode(resp_buf, 0)

        result = UpdateLocationAnswer(
            error_code=0,
            default_context_id=default_context_id,
            total_ambr=UpdateLocationAnswer.AggregatedMaximumBitrate(
                max_bandwidth_ul=total_ambr['ul'],
                max_bandwidth_dl=total_ambr['dl'],
            ),
            msisdn=b'333608050011',
            all_apns_included=all_apns_included,
            apn=[
                UpdateLocationAnswer.APNConfiguration(
                    context_id=apn['context_id'],
                    service_selection=apn['service_selection'],
                    qos_profile=UpdateLocationAnswer.APNConfiguration.
                    QoSProfile(
                        class_id=apn['qos_profile']['class_id'],
                        priority_level=apn['qos_profile']['priority_level'],
                        preemption_capability=apn['qos_profile']
                        ['preemption_capability'],
                        preemption_vulnerability=apn['qos_profile']
                        ['preemption_vulnerability'],
                    ),
                    ambr=UpdateLocationAnswer.AggregatedMaximumBitrate(
                        max_bandwidth_ul=apn['ambr']['ul'],
                        max_bandwidth_dl=apn['ambr']['dl'],
                    ),
                    pdn=UpdateLocationAnswer.APNConfiguration.IPV4,
                ) for apn in apns
            ],
        )
        result_future = unittest.mock.Mock()
        result_future.exception.side_effect = [None]
        result_future.result.side_effect = [result]

        self._server._s6a_manager._relay_update_location_answer(
            state_id,
            req,
            result_future,
            0,
        )
        self._writes.assert_called_once_with(resp_buf)
        self._writes.reset_mock()
Exemple #13
0
class S6AApplication(abc.Application):
    """
    As defined in TS 29.272, the 3GPP S6a/S6d application enables the
    transfer of subscriber-related data between the Mobile Management Entity
    (MME) and the Home Subscriber Server (HSS) on the S6a interface and between
    the Serving GPRS Support Node (SGSN) and the Home Subscriber Server
    (HSS) on the S6d interface.
    """
    # The ID this application uses for messages
    APP_ID = 16777251
    # The Vendor-Specific-Application-Id and VendorId AVPs that
    # the S6a application should advertise
    CAPABILITIES_EXCHANGE_AVPS = [
        avp.AVP('Supported-Vendor-Id', avp.VendorId.TGPP),
        avp.AVP(
            'Vendor-Specific-Application-Id',
            [
                avp.AVP('Auth-Application-Id', APP_ID),
                avp.AVP('Vendor-Id', avp.VendorId.TGPP),
            ],
        ),
    ]
    # Required fields for requests of each command type
    REQUIRED_FIELDS = {
        S6AApplicationCommands.AUTHENTICATION_INFORMATION: [
            'Session-Id',
            'Auth-Session-State',
            'User-Name',
            'Visited-PLMN-Id',
            'Requested-EUTRAN-Authentication-Info',
        ],
        S6AApplicationCommands.UPDATE_LOCATION: [
            'Session-Id',
            'Auth-Session-State',
            'User-Name',
            'Visited-PLMN-Id',
            'RAT-Type',
            'ULR-Flags',
        ],
    }

    def __init__(self, lte_processor, realm, host, host_ip, loop=None):
        """Each application has access to a write stream and a collection of
        settings, currently limited to realm and host

        Args:
            lte_processor: A processor instance
            realm: the realm the application should serve
            host: the host name the application should serve
            host_ip: the IP address of the host
        """
        super(S6AApplication, self).__init__(realm, host, host_ip, loop)
        self.lte_processor = lte_processor

    def handle_msg(self, state_id, msg):
        """
        Handle the command of an incoming S6a/S6d request

        Args:
            state_id: the server state identifier
            msg: the message to handle
        Returns:
            None
        """
        if not msg.header.request:
            logging.warning("Received unsolicited answer")
            return

        if msg.header.command_code == \
                S6AApplicationCommands.AUTHENTICATION_INFORMATION:
            self._send_auth(state_id, msg)
        elif msg.header.command_code == S6AApplicationCommands.UPDATE_LOCATION:
            self._send_location_request(state_id, msg)
        else:
            logging.error('Unsupported command: %d', msg.command_code)

    def validate_message(self, state_id, msg):
        """
        Validate a message and send the appropriate error response
        if necessary

        Args:
            state_id: the server state_id
            msg: the message to validate
        Returns:
            True if the message validated
        """
        # Validate we have all required fields
        required_fields = self.REQUIRED_FIELDS[msg.header.command_code]
        if not msg.has_fields(required_fields):
            logging.error(
                "Missing AVP for s6a command %d",
                msg.header.command_code,
            )
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_MISSING_AVP,
            )
            self.writer.send_msg(resp)
            return False
        return True

    def _gen_response(self, state_id, msg, result_code, body_avps=None):
        """
        Generates response message headers to an incoming request and appends
        the response AVPs in the expected order

        Args:
            state_id: the server state id
            msg: the message to respond to
            result_code: the Result-Code of the response
            body_avps: (optional) the AVPs to include in the response body
        Returns:
            a message instance containing the response
        """
        # Generate response message headers
        if body_avps is None:
            body_avps = []
        resp_msg = message.Message.create_response_msg(msg)

        # Session AVPs must come immediately after header RFC3588 8.8
        resp_msg.append_avp(msg.find_avp(*avp.resolve('Session-Id')))

        for body_avp in body_avps:
            resp_msg.append_avp(body_avp)

        # Auth-Session-State is NO_STATE_MAINTAINED (1)
        resp_msg.append_avp(avp.AVP('Auth-Session-State', 1))

        # Host identifiers
        resp_msg.append_avp(avp.AVP('Origin-Host', self.host))
        resp_msg.append_avp(avp.AVP('Origin-Realm', self.realm))
        resp_msg.append_avp(avp.AVP('Origin-State-Id', state_id))

        # Response result
        resp_msg.append_avp(avp.AVP('Result-Code', result_code))
        return resp_msg

    def _send_auth(self, state_id, msg):
        """
        Handles an incoming 3GPP-Authentication-Information-Request
        and writes a 3GPP-Authentication-Information-Answer

        Args:
            state_id: the server state id
            msg: an auth request message
        Returns:
            None
        """
        # Validate the message
        if not self.validate_message(state_id, msg):
            return
        imsi = ""
        try:
            imsi = msg.find_avp(*avp.resolve('User-Name')).value
            plmn = msg.find_avp(*avp.resolve('Visited-PLMN-Id')).value
            request_eutran_info = msg.find_avp(
                *avp.resolve('Requested-EUTRAN-Authentication-Info'), )
            re_sync_info = request_eutran_info.find_avp(
                *avp.resolve('Re-Synchronization-Info'), )

            if re_sync_info:
                # According to 29.272 7.3.15 this should be concatenation of
                # RAND and AUTS but OAI only sends AUTS so hardcode till fixed
                rand = re_sync_info.value[:16]
                auts = re_sync_info.value[16:]
                self.lte_processor.resync_lte_auth_seq(imsi, rand, auts)

            rand, xres, autn, kasme = \
                self.lte_processor.generate_lte_auth_vector(imsi, plmn)

            auth_info = avp.AVP(
                'Authentication-Info',
                [
                    avp.AVP(
                        'E-UTRAN-Vector',
                        [
                            avp.AVP('RAND', rand),
                            avp.AVP('XRES', xres),
                            avp.AVP('AUTN', autn),
                            avp.AVP('KASME', kasme),
                        ],
                    ),
                ],
            )

            S6A_AUTH_SUCCESS_TOTAL.inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_SUCCESS,
                [auth_info],
            )
            logging.info("Auth success: %s", imsi)
        except CryptoError as e:
            S6A_AUTH_FAILURE_TOTAL.labels(
                code=avp.ResultCode.DIAMETER_AUTHENTICATION_REJECTED, ).inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_AUTHENTICATION_REJECTED,
            )
            logging.error("Auth error for %s: %s", imsi, e)
        except SubscriberNotFoundError as e:
            S6A_AUTH_FAILURE_TOTAL.labels(
                code=avp.ResultCode.DIAMETER_ERROR_USER_UNKNOWN, ).inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_ERROR_USER_UNKNOWN,
            )
            logging.warning("Subscriber not found: %s", e)
        except ServiceNotActive as e:
            S6A_AUTH_FAILURE_TOTAL.labels(
                code=avp.ResultCode.DIAMETER_ERROR_UNAUTHORIZED_SERVICE,
            ).inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_ERROR_UNAUTHORIZED_SERVICE,
            )
            logging.error("Service not active for %s: %s", imsi, e)
        self.writer.send_msg(resp)

    def _send_location_request(self, state_id, msg):
        """
        Handles an incoming 3GPP-Update-Location-Request request and writes a
        3GPP-Update-Location-Answer

        Args:
            state_id: the server state id
            msg: an update location request message
        Returns:
            None
        """
        # Validate the message
        if not self.validate_message(state_id, msg):
            return

        ula_flags = avp.AVP('ULA-Flags', 1)

        try:
            imsi = msg.find_avp(*avp.resolve('User-Name')).value
            profile = self.lte_processor.get_sub_profile(imsi)
        except SubscriberNotFoundError as e:
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_ERROR_USER_UNKNOWN,
            )
            logging.warning('Subscriber not found for ULR: %s', e)
            return

        # Stubbed out Subscription Data from OAI
        subscription_data = avp.AVP(
            'Subscription-Data',
            [
                avp.AVP('MSISDN', b'333608050011'),
                avp.AVP('Access-Restriction-Data', 47),
                avp.AVP('Subscriber-Status', 0),
                avp.AVP('Network-Access-Mode', 2),
                avp.AVP(
                    'AMBR',
                    [
                        avp.AVP('Max-Requested-Bandwidth-UL',
                                profile.max_ul_bit_rate),
                        avp.AVP('Max-Requested-Bandwidth-DL',
                                profile.max_dl_bit_rate),
                    ],
                ),
                avp.AVP(
                    'APN-Configuration-Profile',
                    [
                        avp.AVP('Context-Identifier', 0),
                        avp.AVP('All-APN-Configurations-Included-Indicator',
                                0),
                        avp.AVP(
                            'APN-Configuration',
                            [
                                avp.AVP('Context-Identifier', 0),
                                avp.AVP('PDN-Type', 0),
                                avp.AVP('Service-Selection', 'oai.ipv4'),
                                avp.AVP(
                                    'EPS-Subscribed-QoS-Profile',
                                    [
                                        avp.AVP('QoS-Class-Identifier', 9),
                                        avp.AVP(
                                            'Allocation-Retention-Priority',
                                            [
                                                avp.AVP('Priority-Level', 15),
                                                avp.AVP(
                                                    'Pre-emption-Capability',
                                                    1),
                                                avp.AVP(
                                                    'Pre-emption-Vulnerability',
                                                    0),
                                            ],
                                        ),
                                    ],
                                ),
                                avp.AVP(
                                    'AMBR',
                                    [
                                        avp.AVP(
                                            'Max-Requested-Bandwidth-UL',
                                            profile.max_ul_bit_rate,
                                        ),
                                        avp.AVP(
                                            'Max-Requested-Bandwidth-DL',
                                            profile.max_dl_bit_rate,
                                        ),
                                    ],
                                ),
                            ],
                        ),
                    ],
                ),
            ],
        )

        S6A_LUR_TOTAL.inc()
        resp = self._gen_response(
            state_id,
            msg,
            avp.ResultCode.DIAMETER_SUCCESS,
            [ula_flags, subscription_data],
        )
        self.writer.send_msg(resp)
Exemple #14
0
    def _send_location_request(self, state_id, msg):
        """
        Handles an incoming 3GPP-Update-Location-Request request and writes a
        3GPP-Update-Location-Answer

        Args:
            state_id: the server state id
            msg: an update location request message
        Returns:
            None
        """
        # Validate the message
        if not self.validate_message(state_id, msg):
            return

        ula_flags = avp.AVP('ULA-Flags', 1)

        try:
            imsi = msg.find_avp(*avp.resolve('User-Name')).value
            profile = self.lte_processor.get_sub_profile(imsi)
        except SubscriberNotFoundError as e:
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_ERROR_USER_UNKNOWN,
            )
            logging.warning('Subscriber not found for ULR: %s', e)
            return

        # Stubbed out Subscription Data from OAI
        subscription_data = avp.AVP(
            'Subscription-Data',
            [
                avp.AVP('MSISDN', b'333608050011'),
                avp.AVP('Access-Restriction-Data', 47),
                avp.AVP('Subscriber-Status', 0),
                avp.AVP('Network-Access-Mode', 2),
                avp.AVP(
                    'AMBR',
                    [
                        avp.AVP('Max-Requested-Bandwidth-UL',
                                profile.max_ul_bit_rate),
                        avp.AVP('Max-Requested-Bandwidth-DL',
                                profile.max_dl_bit_rate),
                    ],
                ),
                avp.AVP(
                    'APN-Configuration-Profile',
                    [
                        avp.AVP('Context-Identifier', 0),
                        avp.AVP('All-APN-Configurations-Included-Indicator',
                                0),
                        avp.AVP(
                            'APN-Configuration',
                            [
                                avp.AVP('Context-Identifier', 0),
                                avp.AVP('PDN-Type', 0),
                                avp.AVP('Service-Selection', 'oai.ipv4'),
                                avp.AVP(
                                    'EPS-Subscribed-QoS-Profile',
                                    [
                                        avp.AVP('QoS-Class-Identifier', 9),
                                        avp.AVP(
                                            'Allocation-Retention-Priority',
                                            [
                                                avp.AVP('Priority-Level', 15),
                                                avp.AVP(
                                                    'Pre-emption-Capability',
                                                    1),
                                                avp.AVP(
                                                    'Pre-emption-Vulnerability',
                                                    0),
                                            ],
                                        ),
                                    ],
                                ),
                                avp.AVP(
                                    'AMBR',
                                    [
                                        avp.AVP(
                                            'Max-Requested-Bandwidth-UL',
                                            profile.max_ul_bit_rate,
                                        ),
                                        avp.AVP(
                                            'Max-Requested-Bandwidth-DL',
                                            profile.max_dl_bit_rate,
                                        ),
                                    ],
                                ),
                            ],
                        ),
                    ],
                ),
            ],
        )

        S6A_LUR_TOTAL.inc()
        resp = self._gen_response(
            state_id,
            msg,
            avp.ResultCode.DIAMETER_SUCCESS,
            [ula_flags, subscription_data],
        )
        self.writer.send_msg(resp)
Exemple #15
0
    def _send_auth(self, state_id, msg):
        """
        Handles an incoming 3GPP-Authentication-Information-Request
        and writes a 3GPP-Authentication-Information-Answer

        Args:
            state_id: the server state id
            msg: an auth request message
        Returns:
            None
        """
        # Validate the message
        if not self.validate_message(state_id, msg):
            return
        imsi = ""
        try:
            imsi = msg.find_avp(*avp.resolve('User-Name')).value
            plmn = msg.find_avp(*avp.resolve('Visited-PLMN-Id')).value
            request_eutran_info = msg.find_avp(
                *avp.resolve('Requested-EUTRAN-Authentication-Info'), )
            re_sync_info = request_eutran_info.find_avp(
                *avp.resolve('Re-Synchronization-Info'), )

            if re_sync_info:
                # According to 29.272 7.3.15 this should be concatenation of
                # RAND and AUTS but OAI only sends AUTS so hardcode till fixed
                rand = re_sync_info.value[:16]
                auts = re_sync_info.value[16:]
                self.lte_processor.resync_lte_auth_seq(imsi, rand, auts)

            rand, xres, autn, kasme = \
                self.lte_processor.generate_lte_auth_vector(imsi, plmn)

            auth_info = avp.AVP(
                'Authentication-Info',
                [
                    avp.AVP(
                        'E-UTRAN-Vector',
                        [
                            avp.AVP('RAND', rand),
                            avp.AVP('XRES', xres),
                            avp.AVP('AUTN', autn),
                            avp.AVP('KASME', kasme),
                        ],
                    ),
                ],
            )

            S6A_AUTH_SUCCESS_TOTAL.inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_SUCCESS,
                [auth_info],
            )
            logging.info("Auth success: %s", imsi)
        except CryptoError as e:
            S6A_AUTH_FAILURE_TOTAL.labels(
                code=avp.ResultCode.DIAMETER_AUTHENTICATION_REJECTED, ).inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_AUTHENTICATION_REJECTED,
            )
            logging.error("Auth error for %s: %s", imsi, e)
        except SubscriberNotFoundError as e:
            S6A_AUTH_FAILURE_TOTAL.labels(
                code=avp.ResultCode.DIAMETER_ERROR_USER_UNKNOWN, ).inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_ERROR_USER_UNKNOWN,
            )
            logging.warning("Subscriber not found: %s", e)
        except ServiceNotActive as e:
            S6A_AUTH_FAILURE_TOTAL.labels(
                code=avp.ResultCode.DIAMETER_ERROR_UNAUTHORIZED_SERVICE,
            ).inc()
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_ERROR_UNAUTHORIZED_SERVICE,
            )
            logging.error("Service not active for %s: %s", imsi, e)
        self.writer.send_msg(resp)
    def test_capability_exchange(self):
        """Test that we can respond to capability exchange requests"""
        msg = message.Message()
        msg.header.command_code = base.BaseApplicationCommands.CAPABILITIES_EXCHANGE
        msg.header.request = True
        msg.append_avp(avp.AVP('Host-IP-Address', '127.0.0.1'))
        msg.append_avp(avp.AVP('Inband-Security-Id', 0))
        msg.append_avp(avp.AVP('Supported-Vendor-Id', 0))
        msg.append_avp(avp.AVP('Vendor-Id', 0))
        msg.append_avp(avp.AVP('Vendor-Specific-Application-Id', []))
        # Encode request message into buffer
        req_buf = bytearray(msg.length)
        msg.encode(req_buf, 0)

        msg = message.Message()
        msg.header.command_code = base.BaseApplicationCommands.CAPABILITIES_EXCHANGE
        msg.header.request = False
        msg.append_avp(avp.AVP('Result-Code', avp.ResultCode.DIAMETER_SUCCESS))
        msg.append_avp(avp.AVP('Origin-Host', self._server.host))
        msg.append_avp(avp.AVP('Origin-Realm', self._server.realm))
        msg.append_avp(avp.AVP('Origin-State-Id', self._server.state_id))
        msg.append_avp(avp.AVP('Host-IP-Address', self.HOST_ADDR))
        msg.append_avp(avp.AVP('Vendor-Id', 0))
        msg.append_avp(avp.AVP('Supported-Vendor-Id', avp.VendorId.TGPP))
        msg.append_avp(
            avp.AVP('Vendor-Specific-Application-Id', [
                avp.AVP('Auth-Application-Id', s6a.S6AApplication.APP_ID),
                avp.AVP('Vendor-Id', avp.VendorId.TGPP)
            ]))
        msg.append_avp(avp.AVP('Product-Name', 'magma'))
        # Encode response message into buffer
        resp_buf = bytearray(msg.length)
        msg.encode(resp_buf, 0)

        self._check_reply(req_buf, resp_buf)
Exemple #17
0
class BaseApplication(abc.Application):
    """
    This is where we implement the Diameter Base Protocol Application which
    is defined in RFC6733. All diameters servers should implement this spec.
    The base Diameter protocol concerns itself with establishing connections
    to peers, capabilities negotiation, how messages are sent and routed
    through peers, and how the connections are eventually torn down.

    This implements a subset of the diameter Base Protcol that OAI MME requires
    This includes handling connection initiation and transport failure
    detection via watchdog requests
    """
    # The ID used for common messages addressed to the base application
    APP_ID = 0
    # The Vendor-Specific-Application-Id and VendorId AVPs that
    # the Common Application should advertise
    CAPABILITIES_EXCHANGE_AVPS = [avp.AVP('Vendor-Id', 0)]
    # Required fields for requests of each command type
    REQUIRED_FIELDS = {
        BaseApplicationCommands.CAPABILITIES_EXCHANGE: [
            'Host-IP-Address', 'Inband-Security-Id', 'Vendor-Id',
            'Supported-Vendor-Id', 'Vendor-Specific-Application-Id'
        ],
        BaseApplicationCommands.DEVICE_WATCHDOG: [],
        BaseApplicationCommands.DISCONNECT_PEER: [],
    }

    def __init__(self, realm, host, host_ip):
        """Each application has access to a write stream and a collection of
        settings, currently limited to realm and host

        Args:
            realm: the realm the application should serve
            host: the host name the application should serve
            host_ip: the IP address of the host
        """
        super(BaseApplication, self).__init__(realm, host, host_ip)
        self.applications = []

    def validate_message(self, state_id, msg):
        """
        Validate a message and send the appropriate error response
        if necessary

        Args:
            msg: the message to validate
        Returns:
            True if the message validated
        """
        # Validate we have all required fields
        required_fields = self.REQUIRED_FIELDS[msg.header.command_code]
        if not msg.has_fields(required_fields):
            logging.error("Missing AVP for diameter command %d",
                          msg.header.command_code)
            resp = self._gen_response(state_id, msg,
                                      avp.ResultCode.DIAMETER_MISSING_AVP)
            self.writer.send_msg(resp)
            return False
        return True

    def register(self, application):
        """
        Registers an application for the Diameter Base application to advertise
        in the capabilities exchange

        Args:
            application: an application instance
        Returns:
            None
        Raises:
            TypeError: application is not an Application subtype
        """
        if not issubclass(type(application), abc.Application):
            raise TypeError('Not a valid application')
        self.applications.append(application)

    def handle_msg(self, state_id, msg):
        """
        Handle a message bound for the common application

        Args:
            state_id: the server state id
            msg: the message to handle
        Returns:
            None
        """
        if not msg.header.request:
            logging.warning("Received unsolicited answer")
            return

        if msg.header.command_code == BaseApplicationCommands.CAPABILITIES_EXCHANGE:
            self._send_capabilities(state_id, msg)
        elif msg.header.command_code == BaseApplicationCommands.DEVICE_WATCHDOG:
            self._send_device_watchdog(state_id, msg)
        elif msg.header.command_code == BaseApplicationCommands.DISCONNECT_PEER:
            self._send_disconnect_peer(state_id, msg)
        else:
            logging.error('Unsupported command: %d', msg.header.command_code)

    def _gen_response(self, state_id, msg, result_code, body_avps=None):
        """
        Generate a response message with all the fields that are found
        in base application responses.

        Args:
            state_id: the server state identifer
            msg: the message to respond to
            result_code: the result code to send
            body_avps: the AVPs to include in the response body
        Returns:
            Message instance containing the response
        """
        # Generate an empty message with the response headers to the msg
        if body_avps is None:
            body_avps = []
        resp_msg = message.Message.create_response_msg(msg)

        resp_msg.append_avp(avp.AVP('Result-Code', result_code))
        resp_msg.append_avp(avp.AVP('Origin-Host', self.host))
        resp_msg.append_avp(avp.AVP('Origin-Realm', self.realm))
        resp_msg.append_avp(avp.AVP('Origin-State-Id', state_id))

        # Add body AVPs
        for body_avp in body_avps:
            resp_msg.append_avp(body_avp)
        return resp_msg

    def _send_capabilities(self, state_id, msg):
        """
        Responds to a Capability-Exchange request by sending Vendor specific
        AVPs from the base and registered applications

        Args:
            state_id: the server state identifier
            msg: the message to respond to
        Returns:
            None
        """
        if self.validate_message(state_id, msg):
            # Generate the capabilities body
            body_avps = [avp.AVP('Host-IP-Address', self.host_ip)]
            body_avps.extend(self.CAPABILITIES_EXCHANGE_AVPS)
            for application in self.applications:
                body_avps.extend(application.CAPABILITIES_EXCHANGE_AVPS)
            body_avps.append(avp.AVP('Product-Name', avp.PRODUCT_NAME))
            DIAMETER_CEX_TOTAL.inc()
            resp = self._gen_response(state_id, msg,
                                      avp.ResultCode.DIAMETER_SUCCESS,
                                      body_avps)
            self.writer.send_msg(resp)

    def _send_device_watchdog(self, state_id, msg):
        """
        Responds to a Device-Watchdog requests which are pings to detection
        transport failures

        Args:
            state_id: the server state identifier
            msg: the message to respond to
        Returns:
            None
        """
        if self.validate_message(state_id, msg):
            resp = self._gen_response(state_id, msg,
                                      avp.ResultCode.DIAMETER_SUCCESS)
            DIAMETER_WATCHDOG_TOTAL.inc()
            self.writer.send_msg(resp)

    def _send_disconnect_peer(self, state_id, msg):
        """
        Responds to a Disconnect-Peer-Request. Upon receipt of this
        message, the transport connection is shut down.

        Args:
            state_id: the server state identifier
            msg: the message to respond to
        Returns:
            None
        """
        logging.info('Received disconnect request, state id: %d', state_id)
        if self.validate_message(state_id, msg):
            resp = self._gen_response(state_id, msg,
                                      avp.ResultCode.DIAMETER_SUCCESS)
            DIAMETER_DISCONECT_TOTAL.inc()
            self.writer.send_msg(resp)
Exemple #18
0
    def _relay_update_location_answer(
        self,
        state_id,
        msg,
        answer_future,
        retries_left,
    ):
        err = answer_future.exception()
        if err and retries_left > 0:
            # TODO: retry only on network failure and not application failures
            user_name = msg.find_avp(*avp.resolve('User-Name')).value
            logging.info(
                "Location Update %s Error! [%s] %s, retrying...",
                user_name,
                err.code(),
                err.details(),
            )
            self._send_location_request_with_retries(
                state_id,
                msg,
                retries_left - 1,
            )
            return
        elif err:
            user_name = msg.find_avp(*avp.resolve('User-Name')).value
            logging.warning(
                "Location Update %s Error! [%s] %s",
                user_name,
                err.code(),
                err.details(),
            )
            resp = self._gen_response(
                state_id,
                msg,
                avp.ResultCode.DIAMETER_UNABLE_TO_COMPLY,
            )
        else:
            answer = answer_future.result()
            error_code = answer.error_code
            if error_code:
                result_info = avp.AVP(
                    'Experimental-Result',
                    [
                        avp.AVP('Vendor-Id', 10415),
                        avp.AVP('Experimental-Result-Code', error_code),
                    ],
                )
                resp = self._gen_response(
                    state_id,
                    msg,
                    error_code,
                    [result_info],
                )
            else:

                # Stubbed out Subscription Data from OAI
                subscription_data = avp.AVP(
                    'Subscription-Data',
                    [
                        avp.AVP('MSISDN', answer.msisdn),
                        avp.AVP('Access-Restriction-Data', 47),
                        avp.AVP('Subscriber-Status', 0),
                        avp.AVP('Network-Access-Mode', 2),
                        avp.AVP(
                            'AMBR',
                            [
                                avp.AVP(
                                    'Max-Requested-Bandwidth-UL',
                                    answer.total_ambr.max_bandwidth_ul,
                                ),
                                avp.AVP(
                                    'Max-Requested-Bandwidth-DL',
                                    answer.total_ambr.max_bandwidth_dl,
                                ),
                            ],
                        ),
                        avp.AVP(
                            'APN-Configuration-Profile',
                            [
                                avp.AVP(
                                    'Context-Identifier',
                                    answer.default_context_id,
                                ),
                                avp.AVP(
                                    'All-APN-Configurations-Included-Indicator',
                                    1 if answer.all_apns_included else 0,
                                ),
                                *[
                                    avp.AVP(
                                        'APN-Configuration',
                                        [
                                            avp.AVP('Context-Identifier',
                                                    apn.context_id),
                                            avp.AVP('PDN-Type', apn.pdn),
                                            avp.AVP(
                                                'Service-Selection',
                                                apn.service_selection,
                                            ),
                                            avp.AVP(
                                                'EPS-Subscribed-QoS-Profile',
                                                [
                                                    avp.AVP(
                                                        'QoS-Class-Identifier',
                                                        apn.qos_profile.
                                                        class_id,
                                                    ),
                                                    avp.AVP(
                                                        'Allocation-Retention-Priority',
                                                        [
                                                            avp.AVP(
                                                                'Priority-Level',
                                                                apn.qos_profile.
                                                                priority_level,
                                                            ),
                                                            avp.AVP(
                                                                'Pre-emption-Capability',
                                                                1 if apn.
                                                                qos_profile.
                                                                preemption_capability
                                                                else 0,
                                                            ),
                                                            avp.AVP(
                                                                'Pre-emption-Vulnerability',
                                                                1 if apn.
                                                                qos_profile.
                                                                preemption_vulnerability
                                                                else 0,
                                                            ),
                                                        ],
                                                    ),
                                                ],
                                            ),
                                            avp.AVP(
                                                'AMBR',
                                                [
                                                    avp.AVP(
                                                        'Max-Requested-Bandwidth-UL',
                                                        apn.ambr.
                                                        max_bandwidth_ul,
                                                    ),
                                                    avp.AVP(
                                                        'Max-Requested-Bandwidth-DL',
                                                        apn.ambr.
                                                        max_bandwidth_dl,
                                                    ),
                                                ],
                                            ),
                                        ],
                                    ) for apn in answer.apn
                                ],
                            ],
                        ),
                    ],
                )

                ula_flags = avp.AVP('ULA-Flags', 1)
                resp = self._gen_response(
                    state_id,
                    msg,
                    avp.ResultCode.DIAMETER_SUCCESS,
                    [ula_flags, subscription_data],
                )
        self.writer.send_msg(resp)
        S6A_LUR_TOTAL.inc()