Example #1
0
    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
Example #2
0
    def test_get_avp(self):
        """We can get the first occurance of an AVP by name or code"""
        self.assertEqual(self.msg.find_avp(*avp.resolve('User-Name')).value, 'hello')
        self.assertEqual(self.msg.find_avp(0, 257).value, '127.0.0.1')

        # Doesn't exist so returns None
        self.assertEqual(self.msg.find_avp(0, 1337), None)
Example #3
0
    def _send_location_request_with_retries(self, state_id, msg, retries_left):
        user_name = msg.find_avp(*avp.resolve('User-Name')).value
        visited_plmn = msg.find_avp(*avp.resolve('Visited-PLMN-Id')).value
        ulr_flags = msg.find_avp(*avp.resolve('ULR-Flags')).value

        request = UpdateLocationRequest(
            user_name=user_name,
            visited_plmn=visited_plmn,
            skip_subscriber_data=ulr_flags & 1 << 2,
            initial_attach=ulr_flags & 1 << 5,
        )
        future = self._client.UpdateLocation.future(request, self.grpc_timeout)
        future.add_done_callback(
            lambda answer: self._loop.call_soon_threadsafe(
                self._relay_update_location_answer, state_id, msg, answer,
                retries_left))
Example #4
0
    def test_message_filter_avp(self):
        """We can get a list of AVPs that matches a code and vendor"""
        # Filter for code 1 (User-Name AVP)
        filtered_avps = list(self.msg.filter_avps(avp.VendorId.DEFAULT, 1))
        self.assertEqual(len(filtered_avps), 2)
        self.assertEqual(filtered_avps[0].value, 'hello')
        self.assertEqual(filtered_avps[1].value, 'world')

        # Filter for non-existent
        self.assertEqual(len(list(self.msg.filter_avps(*avp.resolve('Session-Id')))), 0)
Example #5
0
    def _send_auth_with_retries(self, state_id, msg, retries_left):
        user_name = msg.find_avp(*avp.resolve('User-Name')).value
        visited_plmn = msg.find_avp(*avp.resolve('Visited-PLMN-Id')).value
        request_eutran_info = msg.find_avp(
            *avp.resolve('Requested-EUTRAN-Authentication-Info'), )

        num_requested_eutran_vectors = request_eutran_info.find_avp(
            *avp.resolve('Number-Of-Requested-Vectors'), ).value
        immediate_response_preferred = request_eutran_info.find_avp(
            *avp.resolve('Immediate-Response-Preferred'), ).value
        resync_info = request_eutran_info.find_avp(
            *avp.resolve('Re-Synchronization-Info'), )

        request = AuthenticationInformationRequest(
            user_name=user_name,
            visited_plmn=visited_plmn,
            num_requested_eutran_vectors=num_requested_eutran_vectors,
            immediate_response_preferred=immediate_response_preferred,
            resync_info=resync_info.value if resync_info else None,
        )
        future = self._client.AuthenticationInformation.future(
            request,
            self.grpc_timeout,
        )
        future.add_done_callback(
            lambda answer: self._loop.call_soon_threadsafe(
                self._relay_auth_answer,
                state_id,
                msg,
                answer,
                retries_left,
            ), )
Example #6
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)
Example #7
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()
Example #8
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)
Example #9
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)