[Asterisk-code-review] res_pjsip_registrar: Remove unavailable contacts if exceeds max_contacts (asterisk[19])
Friendly Automation
asteriskteam at digium.com
Fri Sep 24 11:48:23 CDT 2021
Friendly Automation has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/16265 )
Change subject: res_pjsip_registrar: Remove unavailable contacts if exceeds max_contacts
......................................................................
res_pjsip_registrar: Remove unavailable contacts if exceeds max_contacts
The behavior of max_contacts and remove_existing are connected. If
remove_existing is enabled, the soonest expiring contacts are removed.
This may occur when there is an unavailable contact. Similarly,
when remove_existing is not enabled, registrations from good
endpoints are rejected in favor of retaining unavailable contacts.
This commit adds a new AOR option remove_unavailable, and the effect
of this setting will depend on remove_existing. If remove_existing
is set to no, we will still remove unavailable contacts when they
exceed max_contacts, if there are any. If remove_existing is set to
yes, we will prioritize the removal of unavailable contacts before
those that are expiring soonest.
ASTERISK-29525
Change-Id: Ia2711b08f2b4d1177411b1be23e970d7fdff5784
---
M configs/samples/pjsip.conf.sample
A contrib/ast-db-manage/config/versions/f56d79a9f337_pjsip_create_remove_unavailable.py
A doc/CHANGES-staging/res_pjsip_registrar.txt
M include/asterisk/res_pjsip.h
M res/res_pjsip.c
M res/res_pjsip/location.c
M res/res_pjsip_registrar.c
7 files changed, 187 insertions(+), 9 deletions(-)
Approvals:
Kevin Harwell: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 874fcc3..80888c3 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -1109,6 +1109,16 @@
; contact created by rewrite_contact that the device is
; refreshing.
; (default: "no")
+;remove_unavailable=no ; If remove_existing is disabled, will allow a registration
+ ; to succeed by removing only unavailable contacts when
+ ; max_contacts is exceeded. This will reject a registration
+ ; that exceeds max_contacts if no unavailable contacts are
+ ; present to remove. If remove_existing is enabled, will
+ ; prioritize removal of unavailable contacts before removing
+ ; expiring soonest. This tames the behavior of remove_existing
+ ; to only remove an available contact if an unavailable one is
+ ; not present.
+ ; (default: "no")
;type= ; Must be of type aor (default: "")
;qualify_frequency=0 ; Interval at which to qualify an AoR via OPTIONS requests.
; (default: "0")
diff --git a/contrib/ast-db-manage/config/versions/f56d79a9f337_pjsip_create_remove_unavailable.py b/contrib/ast-db-manage/config/versions/f56d79a9f337_pjsip_create_remove_unavailable.py
new file mode 100644
index 0000000..fc82e7a
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/f56d79a9f337_pjsip_create_remove_unavailable.py
@@ -0,0 +1,30 @@
+"""pjsip create remove_unavailable
+
+Revision ID: f56d79a9f337
+Revises: c20d6e3992f4
+Create Date: 2021-07-28 02:09:11.082061
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'f56d79a9f337'
+down_revision = 'c20d6e3992f4'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+AST_BOOL_NAME = 'ast_bool_values'
+AST_BOOL_VALUES = [ '0', '1',
+ 'off', 'on',
+ 'false', 'true',
+ 'no', 'yes' ]
+
+def upgrade():
+ ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)
+ op.add_column('ps_aors', sa.Column('remove_unavailable', ast_bool_values))
+
+def downgrade():
+ op.drop_column('ps_aors', 'remove_unavailable')
+ pass
+
diff --git a/doc/CHANGES-staging/res_pjsip_registrar.txt b/doc/CHANGES-staging/res_pjsip_registrar.txt
new file mode 100644
index 0000000..a80f69f
--- /dev/null
+++ b/doc/CHANGES-staging/res_pjsip_registrar.txt
@@ -0,0 +1,7 @@
+Subject: res_pjsip_registrar
+
+Adds new PJSIP AOR option remove_unavailable to either
+remove unavailable contacts when a REGISTER exceeds
+max_contacts when remove_existing is disabled, or
+prioritize unavailable contacts over other existing
+contacts when remove_existing is enabled.
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 351ce09..8c5b775 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -389,6 +389,8 @@
double qualify_timeout;
/*! Voicemail extension to set in Message-Account */
char *voicemail_extension;
+ /*! Whether to remove unavailable contacts over max_contacts at all or first if remove_existing is enabled */
+ unsigned int remove_unavailable;
};
/*!
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 84c2559..a375fe0 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -1997,8 +1997,9 @@
TLS. Unfortunately, refreshing a registration may register a
different contact address and exceed
<replaceable>max_contacts</replaceable>. The
- <replaceable>remove_existing</replaceable> option can help by
- removing the soonest to expire contact(s) over
+ <replaceable>remove_existing</replaceable> and
+ <replaceable>remove_unavailable</replaceable> options can help by
+ removing either the soonest to expire or unavailable contact(s) over
<replaceable>max_contacts</replaceable> which is likely the
old <replaceable>rewrite_contact</replaceable> contact source
address being refreshed.
@@ -2041,6 +2042,26 @@
</para></note>
</description>
</configOption>
+ <configOption name="remove_unavailable" default="no">
+ <synopsis>Determines whether new contacts should replace unavailable ones.</synopsis>
+ <description><para>
+ The effect of this setting depends on the setting of
+ <replaceable>remove_existing</replaceable>.</para>
+ <para>If <replaceable>remove_existing</replaceable> is set to
+ <literal>no</literal> (default), setting remove_unavailable to
+ <literal>yes</literal> will remove only unavailable contacts that exceed
+ <replaceable>max_contacts</replaceable> to allow an incoming
+ REGISTER to complete sucessfully.</para>
+ <para>If <replaceable>remove_existing</replaceable> is set to
+ <literal>yes</literal>, setting remove_unavailable to
+ <literal>yes</literal> will prioritize unavailable contacts for removal
+ instead of just removing the contact that expires the soonest.</para>
+ <note><para>See <replaceable>remove_existing</replaceable> and
+ <replaceable>max_contacts</replaceable> for further information about how
+ these 3 settings interact.
+ </para></note>
+ </description>
+ </configOption>
<configOption name="type">
<synopsis>Must be of type 'aor'.</synopsis>
</configOption>
@@ -2444,6 +2465,9 @@
<parameter name="RemoveExisting">
<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='aor']/configOption[@name='remove_existing']/synopsis/node())"/></para>
</parameter>
+ <parameter name="RemoveUnavailable">
+ <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='aor']/configOption[@name='remove_unavailable']/synopsis/node())"/></para>
+ </parameter>
<parameter name="Mailboxes">
<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='aor']/configOption[@name='mailboxes']/synopsis/node())"/></para>
</parameter>
@@ -2904,6 +2928,9 @@
<parameter name="RemoveExisting">
<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='aor']/configOption[@name='remove_existing']/synopsis/node())"/></para>
</parameter>
+ <parameter name="RemoveUnavailable">
+ <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='aor']/configOption[@name='remove_unavailable']/synopsis/node())"/></para>
+ </parameter>
<parameter name="Mailboxes">
<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='aor']/configOption[@name='mailboxes']/synopsis/node())"/></para>
</parameter>
diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c
index cef5ad7..bae8a2d 100644
--- a/res/res_pjsip/location.c
+++ b/res/res_pjsip/location.c
@@ -1410,6 +1410,7 @@
ast_sorcery_object_field_register(sorcery, "aor", "authenticate_qualify", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, authenticate_qualify));
ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts));
ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing));
+ ast_sorcery_object_field_register(sorcery, "aor", "remove_unavailable", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_unavailable));
ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, contacts_to_str, contacts_to_var_list, 0, 0);
ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
ast_sorcery_object_field_register_custom(sorcery, "aor", "voicemail_extension", "", voicemail_extension_handler, voicemail_extension_to_str, NULL, 0, 0);
diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c
index 6fe4058..605c0fa 100644
--- a/res/res_pjsip_registrar.c
+++ b/res/res_pjsip_registrar.c
@@ -204,6 +204,7 @@
enum contact_delete_type {
CONTACT_DELETE_ERROR,
CONTACT_DELETE_EXISTING,
+ CONTACT_DELETE_UNAVAILABLE,
CONTACT_DELETE_EXPIRE,
CONTACT_DELETE_REQUEST,
CONTACT_DELETE_SHUTDOWN,
@@ -448,6 +449,9 @@
case CONTACT_DELETE_EXISTING:
reason = "remove existing";
break;
+ case CONTACT_DELETE_UNAVAILABLE:
+ reason = "remove unavailable";
+ break;
case CONTACT_DELETE_EXPIRE:
reason = "expiration";
break;
@@ -483,7 +487,48 @@
struct ast_sip_contact *right_contact = right;
/* Sort from soonest to expire to last to expire */
- return ast_tvcmp(left_contact->expiration_time, right_contact->expiration_time);
+ int time_sorted = ast_tvcmp(left_contact->expiration_time, right_contact->expiration_time);
+
+ struct ast_sip_aor *aor = ast_sip_location_retrieve_aor(left_contact->aor);
+ struct ast_sip_contact_status *left_status;
+ struct ast_sip_contact_status *right_status;
+ int remove_unavailable = 0;
+ int left_unreachable;
+ int right_unreachable;
+
+ if (aor) {
+ remove_unavailable = aor->remove_unavailable;
+ ao2_ref(aor, -1);
+ }
+
+ if (!remove_unavailable) {
+ return time_sorted;
+ }
+
+ /* Get contact status if available */
+ left_status = ast_sip_get_contact_status(left_contact);
+ if (!left_status) {
+ return time_sorted;
+ }
+
+ right_status = ast_sip_get_contact_status(right_contact);
+ if (!right_status) {
+ ao2_ref(left_status, -1);
+ return time_sorted;
+ }
+
+ left_unreachable = (left_status->status == UNAVAILABLE);
+ right_unreachable = (right_status->status == UNAVAILABLE);
+ ao2_ref(left_status, -1);
+ ao2_ref(right_status, -1);
+ if (left_unreachable != right_unreachable) {
+ /* Set unavailable contact to top of vector */
+ if (left_unreachable) return -1;
+ if (right_unreachable) return 1;
+ }
+
+ /* Either both available or both unavailable */
+ return time_sorted;
}
static int vec_contact_add(void *obj, void *arg, int flags)
@@ -512,7 +557,7 @@
/*!
* \internal
- * \brief Remove excess existing contacts that expire the soonest.
+ * \brief Remove excess existing contacts that are unavailable or expire soonest.
* \since 13.18.0
*
* \param contacts Container of unmodified contacts that could remove.
@@ -521,7 +566,7 @@
* \return Nothing
*/
static void remove_excess_contacts(struct ao2_container *contacts, struct ao2_container *response_contacts,
- unsigned int to_remove)
+ unsigned int to_remove, unsigned int remove_existing)
{
struct excess_contact_vector contact_vec;
@@ -545,13 +590,17 @@
ast_assert(AST_VECTOR_SIZE(&contact_vec) == to_remove);
to_remove = AST_VECTOR_SIZE(&contact_vec);
- /* Remove the excess contacts that expire the soonest */
+ /* Remove the excess contacts that are unavailable or expire the soonest */
while (to_remove--) {
struct ast_sip_contact *contact;
contact = AST_VECTOR_GET(&contact_vec, to_remove);
- registrar_contact_delete(CONTACT_DELETE_EXISTING, NULL, contact, contact->aor);
+ if (!remove_existing) {
+ registrar_contact_delete(CONTACT_DELETE_UNAVAILABLE, NULL, contact, contact->aor);
+ } else {
+ registrar_contact_delete(CONTACT_DELETE_EXISTING, NULL, contact, contact->aor);
+ }
ao2_unlink(response_contacts, contact);
}
@@ -574,6 +623,29 @@
return 0;
}
+/*! \brief Internal callback function which adds any contact which is unreachable */
+static int registrar_add_unreachable(void *obj, void *arg, int flags)
+{
+ struct ast_sip_contact *contact = obj;
+ struct ao2_container *container = arg;
+ struct ast_sip_contact_status *status;
+ int unreachable;
+
+ status = ast_sip_get_contact_status(contact);
+ if (!status) {
+ return 0;
+ }
+
+ unreachable = (status->status == UNAVAILABLE);
+ ao2_ref(status, -1);
+
+ if (unreachable) {
+ ao2_link(container, contact);
+ }
+
+ return 0;
+}
+
struct aor_core_response {
/*! Tx data to use for statefull response. NULL for stateless response. */
pjsip_tx_data *tdata;
@@ -596,6 +668,7 @@
int permanent = 0;
int contact_count;
struct ao2_container *existing_contacts = NULL;
+ struct ao2_container *unavail_contacts = NULL;
pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr;
struct registrar_contact_details details = { 0, };
pjsip_tx_data *tdata;
@@ -666,6 +739,33 @@
/* Total contacts after this registration */
contact_count = ao2_container_count(contacts) - permanent + added - deleted;
}
+
+ if (contact_count > aor->max_contacts && aor->remove_unavailable) {
+ /* Get unavailable contact total */
+ int unavail_count = 0;
+
+ unavail_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
+ NULL, ast_sorcery_object_id_compare);
+ if (!unavail_contacts) {
+ response->code = 500;
+ pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);
+ return;
+ }
+ ao2_callback(contacts, OBJ_NODATA, registrar_add_unreachable, unavail_contacts);
+ if (unavail_contacts) {
+ unavail_count = ao2_container_count(unavail_contacts);
+ }
+
+ /* Check to see if removing unavailable contacts will help */
+ if (contact_count - unavail_count <= aor->max_contacts) {
+ /* Remove any unavailable contacts */
+ remove_excess_contacts(unavail_contacts, contacts, contact_count - aor->max_contacts, aor->remove_existing);
+ ao2_cleanup(unavail_contacts);
+ /* We're only here if !aor->remove_existing so this count is correct */
+ contact_count = ao2_container_count(contacts) - permanent + added - deleted;
+ }
+ }
+
if (contact_count > aor->max_contacts) {
/* Enforce the maximum number of contacts */
ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
@@ -867,8 +967,9 @@
/* Total contacts after this registration */
contact_count = ao2_container_count(existing_contacts) + updated + added;
if (contact_count > aor->max_contacts) {
- /* Remove excess existing contacts that expire the soonest */
- remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts);
+ /* Remove excess existing contacts that are unavailable or expire soonest */
+ remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts,
+ aor->remove_existing);
}
ao2_ref(existing_contacts, -1);
}
--
To view, visit https://gerrit.asterisk.org/c/asterisk/+/16265
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings
Gerrit-Project: asterisk
Gerrit-Branch: 19
Gerrit-Change-Id: Ia2711b08f2b4d1177411b1be23e970d7fdff5784
Gerrit-Change-Number: 16265
Gerrit-PatchSet: 6
Gerrit-Owner: Joe <ynadiv at corpit.xyz>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at sangoma.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20210924/1f696b79/attachment-0001.html>
More information about the asterisk-code-review
mailing list