|
|
|
@ -17,6 +17,7 @@
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*** MODULEINFO
|
|
|
|
|
<depend>libmnl</depend>
|
|
|
|
|
<depend>pjproject</depend>
|
|
|
|
|
<depend>res_pjsip</depend>
|
|
|
|
|
<use type="module">res_statsd</use>
|
|
|
|
@ -40,6 +41,7 @@
|
|
|
|
|
#include "res_pjsip/include/res_pjsip_private.h"
|
|
|
|
|
#include "asterisk/vector.h"
|
|
|
|
|
#include "asterisk/pbx.h"
|
|
|
|
|
#include "res_pjsip_outbound_registration/volte.h"
|
|
|
|
|
|
|
|
|
|
/*** DOCUMENTATION
|
|
|
|
|
<configInfo name="res_pjsip_outbound_registration" language="en_US">
|
|
|
|
@ -212,6 +214,9 @@
|
|
|
|
|
<configOption name="manual_register">
|
|
|
|
|
<synopsis>Perform registration only upon request over AMI interface.</synopsis>
|
|
|
|
|
</configOption>
|
|
|
|
|
<configOption name="ims_aka">
|
|
|
|
|
<synopsis>Perform Voice over LTE SIP registration process.</synopsis>
|
|
|
|
|
</configOption>
|
|
|
|
|
</configObject>
|
|
|
|
|
</configFile>
|
|
|
|
|
</configInfo>
|
|
|
|
@ -268,6 +273,8 @@
|
|
|
|
|
/* forward declarations */
|
|
|
|
|
static int set_outbound_initial_authentication_credentials(pjsip_regc *regc,
|
|
|
|
|
const struct ast_sip_auth_vector *auth_vector);
|
|
|
|
|
static int volte_add_outbound_initial_authorization(pjsip_tx_data *tdata, const char *fromdomain,
|
|
|
|
|
const struct ast_sip_auth_vector *auth_vector);
|
|
|
|
|
|
|
|
|
|
/*! \brief Some thread local storage used to determine if the running thread invoked the callback */
|
|
|
|
|
AST_THREADSTORAGE(register_callback_invoked);
|
|
|
|
@ -373,6 +380,28 @@ struct sip_outbound_registration {
|
|
|
|
|
unsigned int support_outbound;
|
|
|
|
|
/*! \brief Do not trigger registration automatically. */
|
|
|
|
|
unsigned int manual_register;
|
|
|
|
|
/*! \brief VoLTE support */
|
|
|
|
|
unsigned int ims_aka;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! \brief States of the VoLTE registration process */
|
|
|
|
|
enum volte_state {
|
|
|
|
|
/* !\brief Send first registration. */
|
|
|
|
|
VOLTE_STATE_REGISTER,
|
|
|
|
|
/* !\brief Wait for SIM keys to be provided. */
|
|
|
|
|
VOLTE_STATE_SIM_REQUEST,
|
|
|
|
|
/* !\brief SIM responded with keys. */
|
|
|
|
|
VOLTE_STATE_SIM_RESPONSE,
|
|
|
|
|
/* !\brief SIM responded with resync token. */
|
|
|
|
|
VOLTE_STATE_SIM_RESYNC,
|
|
|
|
|
/* !\brief SIM responded with failure. */
|
|
|
|
|
VOLTE_STATE_SIM_FAILED,
|
|
|
|
|
/* !\brief Send registration with authentication response. */
|
|
|
|
|
VOLTE_STATE_RESPONSE,
|
|
|
|
|
/* !\brief Send registration with resync token. */
|
|
|
|
|
VOLTE_STATE_RESYNC,
|
|
|
|
|
/* !\breif IMS registration process failed. */
|
|
|
|
|
VOLTE_STATE_FAILED,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! \brief Outbound registration client state information (persists for lifetime of regc) */
|
|
|
|
@ -438,6 +467,16 @@ struct sip_outbound_registration_client_state {
|
|
|
|
|
char *registration_name;
|
|
|
|
|
/*! \brief Expected time of registration lapse/expiration */
|
|
|
|
|
unsigned int registration_expires;
|
|
|
|
|
/*! \brief VoLTE support */
|
|
|
|
|
unsigned int ims_aka;
|
|
|
|
|
/*! \brief Current state of registration process */
|
|
|
|
|
enum volte_state volte_state;
|
|
|
|
|
/*! \brief Current pending respones, while waiting for USIM data */
|
|
|
|
|
struct registration_response *volte_response;
|
|
|
|
|
/*! \brief Current states related to VoLTE process */
|
|
|
|
|
struct volte_states volte;
|
|
|
|
|
/*! \brief Non-zero if we have attempted sending a REGISTER with resync */
|
|
|
|
|
unsigned int resync_attempted:1;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
|
|
|
|
@ -721,6 +760,76 @@ static void add_security_headers(struct sip_outbound_registration_client_state *
|
|
|
|
|
ao2_cleanup(reg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static pj_status_t volte_registration_client(struct sip_outbound_registration_client_state *client_state,
|
|
|
|
|
pjsip_tx_data *tdata)
|
|
|
|
|
{
|
|
|
|
|
struct sip_outbound_registration *reg = NULL;
|
|
|
|
|
struct ast_sip_endpoint *endpt = NULL;
|
|
|
|
|
struct ast_sip_transport *transp = NULL;
|
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
|
|
reg = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "registration",
|
|
|
|
|
client_state->registration_name);
|
|
|
|
|
if (!reg) {
|
|
|
|
|
ast_log(LOG_ERROR, "Internal error\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!reg->endpoint ||
|
|
|
|
|
(!(endpt = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", reg->endpoint)))) {
|
|
|
|
|
ast_log(LOG_ERROR, "No endpoint configured in registration config for '%s'\n",
|
|
|
|
|
client_state->registration_name);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!reg->transport ||
|
|
|
|
|
(!(transp = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", reg->transport)))) {
|
|
|
|
|
ast_log(LOG_ERROR, "No transport configured in registration config for '%s'\n",
|
|
|
|
|
client_state->registration_name);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!endpt->fromdomain) {
|
|
|
|
|
ast_log(LOG_ERROR, "No from_domain defined in endpoint config for '%s'\n", reg->endpoint);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!transp->ims_port_c || !transp->ims_port_s) {
|
|
|
|
|
ast_log(LOG_ERROR, "No ims_port_c or ims_port_s defined in transport config for '%s'\n",
|
|
|
|
|
reg->transport);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (volte_del_authorization(tdata)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to remove authorization header.\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (volte_add_outbound_initial_authorization(tdata, endpt->fromdomain, &client_state->outbound_auths)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to add initial authorization header.\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (volte_add_sec_agree(tdata)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to add sec agree header.\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (volte_reset_transport(&client_state->volte)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to reset transport. Ignoring!\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (volte_add_security_client(&client_state->volte, tdata, transp->ims_port_c, transp->ims_port_s)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to add security client header.\n");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
ao2_cleanup(reg);
|
|
|
|
|
ao2_cleanup(endpt);
|
|
|
|
|
ao2_cleanup(transp);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Helper function which sends a message and cleans up, if needed, on failure */
|
|
|
|
|
static pj_status_t registration_client_send(struct sip_outbound_registration_client_state *client_state,
|
|
|
|
|
pjsip_tx_data *tdata)
|
|
|
|
@ -757,6 +866,15 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
|
|
|
|
|
pjsip_regc_set_transport(client_state->client, &selector);
|
|
|
|
|
ast_sip_tpselector_unref(&selector);
|
|
|
|
|
|
|
|
|
|
/* Create initial IMS headers and reset transport. */
|
|
|
|
|
if (client_state->ims_aka && client_state->volte_state == VOLTE_STATE_REGISTER) {
|
|
|
|
|
if (volte_registration_client(client_state, tdata)) {
|
|
|
|
|
pjsip_tx_data_dec_ref(tdata);
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = pjsip_regc_send(client_state->client, tdata);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -865,29 +983,13 @@ static int handle_client_registration(void *data)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_state->volte_state = VOLTE_STATE_REGISTER;
|
|
|
|
|
|
|
|
|
|
registration_client_send(client_state, tdata);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Timer callback function, used just for registrations */
|
|
|
|
|
static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
|
|
|
|
|
{
|
|
|
|
|
struct sip_outbound_registration_client_state *client_state = entry->user_data;
|
|
|
|
|
|
|
|
|
|
entry->id = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Transfer client_state reference to serializer task so the
|
|
|
|
|
* nominal path will not dec the client_state ref in this
|
|
|
|
|
* pjproject callback thread.
|
|
|
|
|
*/
|
|
|
|
|
if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Scheduled outbound registration could not be executed.\n");
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
|
|
|
|
|
static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
|
|
|
|
|
{
|
|
|
|
@ -947,6 +1049,9 @@ static int handle_client_state_destruction(void *data)
|
|
|
|
|
|
|
|
|
|
cancel_registration(client_state);
|
|
|
|
|
|
|
|
|
|
/* Cleanup IPSec translation. */
|
|
|
|
|
volte_cleanup_xfrm(&client_state->volte);
|
|
|
|
|
|
|
|
|
|
if (client_state->client) {
|
|
|
|
|
pjsip_regc_info info;
|
|
|
|
|
pjsip_tx_data *tdata;
|
|
|
|
@ -1018,8 +1123,23 @@ struct registration_response {
|
|
|
|
|
pjsip_tx_data *old_request;
|
|
|
|
|
/*! \brief Key for the reliable transport in use */
|
|
|
|
|
char transport_key[IP6ADDR_COLON_PORT_BUFLEN];
|
|
|
|
|
/*! \breif USIM authentication response */
|
|
|
|
|
uint8_t sim_res[8];
|
|
|
|
|
uint8_t sim_ik[16];
|
|
|
|
|
uint8_t sim_ck[16];
|
|
|
|
|
uint8_t sim_auts[14];
|
|
|
|
|
/*! \brief Timer for USIM reply timeout */
|
|
|
|
|
pj_timer_entry sim_timer;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*! \brief Helper function which cancels the timer on registration response */
|
|
|
|
|
static void cancel_sim_timer(struct registration_response *response)
|
|
|
|
|
{
|
|
|
|
|
if (pj_timer_heap_cancel_if_active(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()),
|
|
|
|
|
&response->sim_timer, response->sim_timer.id)) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Registration response structure destructor */
|
|
|
|
|
static void registration_response_destroy(void *obj)
|
|
|
|
|
{
|
|
|
|
@ -1033,9 +1153,30 @@ static void registration_response_destroy(void *obj)
|
|
|
|
|
pjsip_tx_data_dec_ref(response->old_request);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response == response->client_state->volte_response)
|
|
|
|
|
response->client_state->volte_response = NULL;
|
|
|
|
|
|
|
|
|
|
cancel_sim_timer(response);
|
|
|
|
|
|
|
|
|
|
ao2_cleanup(response->client_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Timer callback function, used just for registrations */
|
|
|
|
|
static void sim_timeout_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
|
|
|
|
|
{
|
|
|
|
|
struct registration_response *response = entry->user_data;
|
|
|
|
|
|
|
|
|
|
ast_log(LOG_ERROR, "Sim did not respond, authentication failed.\n");
|
|
|
|
|
|
|
|
|
|
if (response->client_state->destroy) {
|
|
|
|
|
/* We have a pending deferred destruction to complete now. */
|
|
|
|
|
ao2_ref(response->client_state, +1);
|
|
|
|
|
handle_client_state_destruction(response->client_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_ref(response, -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Helper function which determines if a response code is temporal or not */
|
|
|
|
|
static int sip_outbound_registration_is_temporal(unsigned int code,
|
|
|
|
|
struct sip_outbound_registration_client_state *client_state)
|
|
|
|
@ -1222,6 +1363,153 @@ static void save_response_fields_to_transport(struct registration_response *resp
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ast_sip_auth *volte_get_sip_auth(const struct ast_sip_auth_vector *auth_vector)
|
|
|
|
|
{
|
|
|
|
|
size_t auth_size = AST_VECTOR_SIZE(auth_vector);
|
|
|
|
|
struct ast_sip_auth *auths[auth_size], *auth = NULL;
|
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
|
|
memset(auths, 0, sizeof(auths));
|
|
|
|
|
if (ast_sip_retrieve_auths(auth_vector, auths)) {
|
|
|
|
|
ast_log(LOG_ERROR, "No authentication vector found. Please configure authentication for IMS\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (idx = 0; idx < auth_size; ++idx) {
|
|
|
|
|
if (auths[idx]->type == AST_SIP_AUTH_TYPE_IMS_AKA)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idx == auth_size) {
|
|
|
|
|
ast_log(LOG_ERROR, "No authentication vector found with type=ims_aka. Please fix config.\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auth = auths[idx];
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
ast_sip_cleanup_auths(auths, auth_size);
|
|
|
|
|
return auth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int handle_volte_unauthorized(struct registration_response *response, uint8_t *out_auts)
|
|
|
|
|
{
|
|
|
|
|
struct security_server sec;
|
|
|
|
|
struct ast_sip_auth *auth;
|
|
|
|
|
pj_str_t algo;
|
|
|
|
|
uint8_t rand[16], autn[16], out_ik[16], out_ck[16];
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
if (!(auth = volte_get_sip_auth(&response->client_state->outbound_auths)))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
switch (response->client_state->volte_state) {
|
|
|
|
|
case VOLTE_STATE_SIM_RESPONSE:
|
|
|
|
|
ast_debug(1, "Processing Authentication response from SIM\n");
|
|
|
|
|
/* Registration response from SIM */
|
|
|
|
|
memcpy(auth->ims_res, response->sim_res, 8);
|
|
|
|
|
memcpy(out_ik, response->sim_ik, 16);
|
|
|
|
|
memcpy(out_ck, response->sim_ck, 16);
|
|
|
|
|
response->client_state->volte_response = NULL;
|
|
|
|
|
rc = 0;
|
|
|
|
|
break;
|
|
|
|
|
case VOLTE_STATE_SIM_RESYNC:
|
|
|
|
|
ast_debug(1, "Processing resync response from SIM\n");
|
|
|
|
|
response->client_state->volte_response = NULL;
|
|
|
|
|
memcpy(out_auts, response->sim_auts, 14);
|
|
|
|
|
rc = -EAGAIN;
|
|
|
|
|
break;
|
|
|
|
|
case VOLTE_STATE_SIM_FAILED:
|
|
|
|
|
ast_debug(1, "Processing failure response from SIM\n");
|
|
|
|
|
response->client_state->volte_response = NULL;
|
|
|
|
|
rc = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ast_debug(1, "Processing REGISTER response from IMS\n");
|
|
|
|
|
/* Remove existing autorization header. */
|
|
|
|
|
if (volte_del_authorization(response->old_request)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to remove authorization header.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
/* Get security server */
|
|
|
|
|
if (volte_get_security_server(&response->client_state->volte, response->rdata, &sec)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to parse the security server header.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (volte_get_auth(&response->client_state->volte, response->rdata,
|
|
|
|
|
(response->code == 401) ? PJSIP_H_WWW_AUTHENTICATE : PJSIP_H_PROXY_AUTHENTICATE,
|
|
|
|
|
&algo, rand, autn)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to parse the authenticate header.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (auth->usim_ami) {
|
|
|
|
|
pj_time_val delay = { .sec = 1, };
|
|
|
|
|
ast_debug(1, "Asking SIM card via AMI to authenticate with the callenge.\n");
|
|
|
|
|
volte_send_authrequest(response->client_state->registration_name, &algo, rand, autn);
|
|
|
|
|
response->client_state->volte_response = response;
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_SIM_REQUEST;
|
|
|
|
|
if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &response->sim_timer, &delay) != PJ_SUCCESS) {
|
|
|
|
|
ast_log(LOG_WARNING, "Failed to schedule SIM response timer\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
rc = volte_authenticate(&response->client_state->volte, auth->usim_opc, auth->usim_k,
|
|
|
|
|
auth->usim_sqn, rand, autn, (uint8_t *)auth->ims_res,
|
|
|
|
|
out_ik, out_ck, out_auts);
|
|
|
|
|
}
|
|
|
|
|
if (rc == -EAGAIN) {
|
|
|
|
|
if (response->client_state->resync_attempted) {
|
|
|
|
|
ast_log(LOG_ERROR, "SQN out of sequence again, aborting.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ast_log(LOG_WARNING, "SQN out of sequence, syncing.\n");
|
|
|
|
|
auth->ims_res_len = 0;
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_RESYNC;
|
|
|
|
|
response->client_state->auth_attempted = 0;
|
|
|
|
|
response->client_state->resync_attempted = 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (rc) {
|
|
|
|
|
ast_log(LOG_ERROR, "Authentication failed.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
auth->ims_res_len = 8;
|
|
|
|
|
|
|
|
|
|
if (volte_set_transport(&response->client_state->volte, response->old_request, &sec.alg, &sec.ealg,
|
|
|
|
|
out_ik, pj_strtoul(&sec.spi_c), pj_strtoul(&sec.spi_s),
|
|
|
|
|
pj_strtoul(&sec.port_c), pj_strtoul(&sec.port_s))) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to set transport.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (volte_add_security_verify(&response->client_state->volte, response->old_request)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to add security verify.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_RESPONSE;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Timer callback function, used just for registrations */
|
|
|
|
|
static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
|
|
|
|
|
{
|
|
|
|
|
struct sip_outbound_registration_client_state *client_state = entry->user_data;
|
|
|
|
|
|
|
|
|
|
entry->id = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Transfer client_state reference to serializer task so the
|
|
|
|
|
* nominal path will not dec the client_state ref in this
|
|
|
|
|
* pjproject callback thread.
|
|
|
|
|
*/
|
|
|
|
|
if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Scheduled outbound registration could not be executed.\n");
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Callback function for handling a response to a registration attempt */
|
|
|
|
|
static int handle_registration_response(void *data)
|
|
|
|
@ -1230,6 +1518,7 @@ static int handle_registration_response(void *data)
|
|
|
|
|
pjsip_regc_info info;
|
|
|
|
|
char server_uri[PJSIP_MAX_URL_SIZE];
|
|
|
|
|
char client_uri[PJSIP_MAX_URL_SIZE];
|
|
|
|
|
uint8_t auts[14];
|
|
|
|
|
|
|
|
|
|
if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
|
|
|
|
|
ao2_ref(response, -1);
|
|
|
|
@ -1244,6 +1533,18 @@ static int handle_registration_response(void *data)
|
|
|
|
|
ast_debug(1, "Processing REGISTER response %d from server '%s' for client '%s'\n",
|
|
|
|
|
response->code, server_uri, client_uri);
|
|
|
|
|
|
|
|
|
|
if ((response->code == 401 || response->code == 407) && response->client_state->ims_aka) {
|
|
|
|
|
if (handle_volte_unauthorized(response, auts)) {
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_FAILED;
|
|
|
|
|
goto volte_failed;
|
|
|
|
|
}
|
|
|
|
|
/* Wait for the SIM to respond. Store registration_response to client state. */
|
|
|
|
|
if (response->client_state->volte_state == VOLTE_STATE_SIM_REQUEST) {
|
|
|
|
|
response->client_state->volte_response = response;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response->code == 408 || response->code == 503) {
|
|
|
|
|
if ((ast_sip_failover_request(response->old_request))) {
|
|
|
|
|
int res = registration_client_send(response->client_state, response->old_request);
|
|
|
|
@ -1303,7 +1604,14 @@ static int handle_registration_response(void *data)
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (!ast_sip_create_request_with_auth(&response->client_state->outbound_auths,
|
|
|
|
|
response->rdata, response->old_request, &tdata)) {
|
|
|
|
|
response->client_state->auth_attempted = 1;
|
|
|
|
|
if (response->client_state->volte_state == VOLTE_STATE_RESYNC) {
|
|
|
|
|
if (volte_add_auts(&response->client_state->volte, tdata, auts)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Failed to add authentication token.\n");
|
|
|
|
|
goto volte_failed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!response->client_state->resync_attempted)
|
|
|
|
|
response->client_state->auth_attempted = 1;
|
|
|
|
|
ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",
|
|
|
|
|
server_uri, client_uri);
|
|
|
|
|
pjsip_tx_data_add_ref(tdata);
|
|
|
|
@ -1325,8 +1633,11 @@ static int handle_registration_response(void *data)
|
|
|
|
|
}
|
|
|
|
|
/* Otherwise, fall through so the failure is processed appropriately */
|
|
|
|
|
}
|
|
|
|
|
volte_failed:
|
|
|
|
|
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_REGISTER;
|
|
|
|
|
response->client_state->auth_attempted = 0;
|
|
|
|
|
response->client_state->resync_attempted = 0;
|
|
|
|
|
|
|
|
|
|
if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
|
|
|
|
|
/* Check if this is in regards to registering or unregistering */
|
|
|
|
@ -1420,6 +1731,17 @@ static int handle_registration_response(void *data)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int queue_authresponse(struct sip_outbound_registration_state *state)
|
|
|
|
|
{
|
|
|
|
|
if (ast_sip_push_task(state->client_state->serializer, handle_registration_response, state->client_state->volte_response)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
|
|
|
|
|
ao2_cleanup(state->client_state->volte_response);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Callback function for outbound registration client */
|
|
|
|
|
static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
|
|
|
|
|
{
|
|
|
|
@ -1434,6 +1756,12 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
|
|
|
|
|
|
|
|
|
|
*callback_invoked = 1;
|
|
|
|
|
|
|
|
|
|
/* Cleanup pending ims response. */
|
|
|
|
|
if (client_state->volte_response) {
|
|
|
|
|
ao2_cleanup(client_state->volte_response);
|
|
|
|
|
client_state->volte_response = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response = ao2_alloc(sizeof(*response), registration_response_destroy);
|
|
|
|
|
if (!response) {
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
@ -1447,6 +1775,7 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
|
|
|
|
|
* pjproject callback thread.
|
|
|
|
|
*/
|
|
|
|
|
response->client_state = client_state;
|
|
|
|
|
pj_timer_entry_init(&response->sim_timer, 0, response, sim_timeout_cb);
|
|
|
|
|
|
|
|
|
|
ast_debug(1, "Received REGISTER response %d(%.*s)\n",
|
|
|
|
|
param->code, (int) param->reason.slen, param->reason.ptr);
|
|
|
|
@ -1522,6 +1851,10 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
|
|
|
|
|
ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
|
|
|
|
|
sip_outbound_registration_status_str(client_state->status));
|
|
|
|
|
|
|
|
|
|
/* In case there is an unfinished response, destroy it. */
|
|
|
|
|
if (client_state->volte_response)
|
|
|
|
|
ao2_cleanup(client_state->volte_response);
|
|
|
|
|
|
|
|
|
|
ast_taskprocessor_unreference(client_state->serializer);
|
|
|
|
|
ast_free(client_state->transport_name);
|
|
|
|
|
ast_free(client_state->registration_name);
|
|
|
|
@ -1553,6 +1886,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
|
|
|
|
|
state->client_state->transport_name = ast_strdup(registration->transport);
|
|
|
|
|
state->client_state->registration_name =
|
|
|
|
|
ast_strdup(ast_sorcery_object_get_id(registration));
|
|
|
|
|
state->client_state->ims_aka = registration->ims_aka;
|
|
|
|
|
|
|
|
|
|
ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
|
|
|
|
|
ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
|
|
|
|
@ -1820,6 +2154,20 @@ cleanup:
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add intial authorization header for IMS AKA */
|
|
|
|
|
static int volte_add_outbound_initial_authorization(pjsip_tx_data *tdata, const char *fromdomain,
|
|
|
|
|
const struct ast_sip_auth_vector *auth_vector)
|
|
|
|
|
{
|
|
|
|
|
struct ast_sip_auth *auth;
|
|
|
|
|
|
|
|
|
|
if (!(auth = volte_get_sip_auth(auth_vector)))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
volte_init_authorization(tdata, fromdomain, auth->auth_user);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Helper function that allocates a pjsip registration client and configures it */
|
|
|
|
|
static int sip_outbound_registration_regc_alloc(void *data)
|
|
|
|
|
{
|
|
|
|
@ -1993,8 +2341,12 @@ static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, vo
|
|
|
|
|
ast_log(LOG_ERROR, "Line support has been enabled on outbound registration '%s' without providing an endpoint\n",
|
|
|
|
|
ast_sorcery_object_get_id(applied));
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (!ast_strlen_zero(applied->endpoint) && !applied->line) {
|
|
|
|
|
ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line support\n",
|
|
|
|
|
} else if (applied->ims_aka && ast_strlen_zero(applied->endpoint)) {
|
|
|
|
|
ast_log(LOG_ERROR, "IMS AKA support has been enabled on outbound registration '%s' without providing an endpoint\n",
|
|
|
|
|
ast_sorcery_object_get_id(applied));
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (!ast_strlen_zero(applied->endpoint) && !applied->line && !applied->ims_aka) {
|
|
|
|
|
ast_log(LOG_ERROR, "An endpoint has been specified on outbound registration '%s' without enabling line or IMS AKA support\n",
|
|
|
|
|
ast_sorcery_object_get_id(applied));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
@ -2380,6 +2732,90 @@ static int ami_register(struct mansession *s, const struct message *m)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ami_authresponse(struct mansession *s, const struct message *m)
|
|
|
|
|
{
|
|
|
|
|
const char *registration_name = astman_get_header(m, "Registration");
|
|
|
|
|
const char *res_str = astman_get_header(m, "RES");
|
|
|
|
|
const char *ik_str = astman_get_header(m, "IK");
|
|
|
|
|
const char *ck_str = astman_get_header(m, "CK");
|
|
|
|
|
const char *auts_str = astman_get_header(m, "AUTS");
|
|
|
|
|
struct sip_outbound_registration_state *state;
|
|
|
|
|
struct registration_response *response;
|
|
|
|
|
|
|
|
|
|
if (ast_strlen_zero(registration_name)) {
|
|
|
|
|
ast_log(LOG_ERROR, "SIM card responded: Registration parameter missing.\n");
|
|
|
|
|
astman_send_error(s, m, "Registration parameter missing");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state = get_state(registration_name);
|
|
|
|
|
if (!state) {
|
|
|
|
|
ast_log(LOG_ERROR, "SIM card responded: Unable to retrieve registration entry.\n");
|
|
|
|
|
astman_send_error(s, m, "Unable to retrieve registration entry\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (!state->client_state || !state->client_state->volte_response) {
|
|
|
|
|
ast_debug(1, "SIM card responded: No pending AuthRequest.\n");
|
|
|
|
|
astman_send_error(s, m, "No pending AuthRequest\n");
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
response = state->client_state->volte_response;
|
|
|
|
|
|
|
|
|
|
ast_debug(1, "SIM card responded. RES=%s IK=%s CK=%s AUTS=%s\n", res_str, ik_str, ck_str, auts_str);
|
|
|
|
|
|
|
|
|
|
cancel_sim_timer(response);
|
|
|
|
|
|
|
|
|
|
if (res_str[0] && ik_str[0] && ck_str[0] && !auts_str[0]) {
|
|
|
|
|
if (volte_hex_to_octet_string("RES", res_str, response->sim_res, sizeof(response->sim_res))) {
|
|
|
|
|
ast_log(LOG_ERROR, "SIM card responded: RES value invalid.\n");
|
|
|
|
|
astman_send_error(s, m, "RES value invalid\n");
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (volte_hex_to_octet_string("IK", ik_str, response->sim_ik, sizeof(response->sim_ik))) {
|
|
|
|
|
ast_log(LOG_ERROR, "SIM card responded: IK value invalid.\n");
|
|
|
|
|
astman_send_error(s, m, "IK value invalid\n");
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (volte_hex_to_octet_string("CK", ck_str, response->sim_ck, sizeof(response->sim_ck))) {
|
|
|
|
|
ast_log(LOG_ERROR, "SIM card responded: CK value invalid.\n");
|
|
|
|
|
astman_send_error(s, m, "CK value invalid\n");
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_SIM_RESPONSE;
|
|
|
|
|
} else if (!res_str[0] && !ik_str[0] && !ck_str[0] && auts_str[0]) {
|
|
|
|
|
if (volte_hex_to_octet_string("AUTS", auts_str, response->sim_auts, sizeof(response->sim_auts))) {
|
|
|
|
|
ast_log(LOG_ERROR, "SIM card responded: AUTS value invalid.\n");
|
|
|
|
|
astman_send_error(s, m, "AUTS value invalid\n");
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_SIM_RESYNC;
|
|
|
|
|
} else if (!res_str[0] && !ik_str[0] && !ck_str[0] && !auts_str[0]) {
|
|
|
|
|
response->client_state->volte_state = VOLTE_STATE_SIM_FAILED;
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_ERROR, "SIM card responded: Missing or too many AuthResponse values.\n");
|
|
|
|
|
astman_send_error(s, m, "Missing or too many AuthResponse values\n");
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We need to serialize the unregister and register so they need
|
|
|
|
|
* to be queued as separate tasks.
|
|
|
|
|
*/
|
|
|
|
|
if (queue_authresponse(state)) {
|
|
|
|
|
astman_send_ack(s, m, "Failed to queue AuthResponse");
|
|
|
|
|
} else {
|
|
|
|
|
astman_send_ack(s, m, "AuthResponse sent");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct sip_ami_outbound {
|
|
|
|
|
struct ast_sip_ami *ami;
|
|
|
|
|
int registered;
|
|
|
|
@ -2723,6 +3159,7 @@ static int unload_module(void)
|
|
|
|
|
ast_manager_unregister("PJSIPShowRegistrationsOutbound");
|
|
|
|
|
ast_manager_unregister("PJSIPUnregister");
|
|
|
|
|
ast_manager_unregister("PJSIPRegister");
|
|
|
|
|
ast_manager_unregister("AuthResponse");
|
|
|
|
|
|
|
|
|
|
ast_cli_unregister_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
|
|
|
|
|
ast_sip_unregister_cli_formatter(cli_formatter);
|
|
|
|
@ -2758,6 +3195,8 @@ static int unload_module(void)
|
|
|
|
|
ao2_cleanup(shutdown_group);
|
|
|
|
|
shutdown_group = NULL;
|
|
|
|
|
|
|
|
|
|
g_volte_exit();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2814,6 +3253,7 @@ static int load_module(void)
|
|
|
|
|
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "line", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, line));
|
|
|
|
|
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, endpoint));
|
|
|
|
|
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "manual_register", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, manual_register));
|
|
|
|
|
ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "ims_aka", "no", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, ims_aka));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Register sorcery observers.
|
|
|
|
@ -2849,10 +3289,17 @@ static int load_module(void)
|
|
|
|
|
ast_sip_register_cli_formatter(cli_formatter);
|
|
|
|
|
ast_cli_register_multiple(cli_outbound_registration, ARRAY_LEN(cli_outbound_registration));
|
|
|
|
|
|
|
|
|
|
/* Init VoLTE process. */
|
|
|
|
|
if (g_volte_init()) {
|
|
|
|
|
unload_module();
|
|
|
|
|
return AST_MODULE_LOAD_DECLINE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Register AMI actions. */
|
|
|
|
|
ast_manager_register_xml("PJSIPUnregister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_unregister);
|
|
|
|
|
ast_manager_register_xml("PJSIPRegister", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_register);
|
|
|
|
|
ast_manager_register_xml("PJSIPShowRegistrationsOutbound", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_show_outbound_registrations);
|
|
|
|
|
ast_manager_register_xml_core("AuthResponse", 0, ami_authresponse);
|
|
|
|
|
|
|
|
|
|
/* Clear any previous statsd gauges in case we weren't shutdown cleanly */
|
|
|
|
|
ast_statsd_log("PJSIP.registrations.count", AST_STATSD_GAUGE, 0);
|
|
|
|
|