mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	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
This commit is contained in:
		
				
					committed by
					
						 Friendly Automation
						Friendly Automation
					
				
			
			
				
	
			
			
			
						parent
						
							35a94ec708
						
					
				
				
					commit
					6a04c43035
				
			| @@ -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> | ||||
|   | ||||
| @@ -1410,6 +1410,7 @@ int ast_sip_initialize_sorcery_location(void) | ||||
| 	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); | ||||
|   | ||||
| @@ -204,6 +204,7 @@ static int registrar_validate_contacts(const pjsip_rx_data *rdata, pj_pool_t *po | ||||
| 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 @@ static int registrar_contact_delete(enum contact_delete_type type, pjsip_transpo | ||||
| 			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 @@ static int vec_contact_cmp(struct ast_sip_contact *left, struct ast_sip_contact | ||||
| 	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 @@ static int vec_contact_add(void *obj, void *arg, int flags) | ||||
|  | ||||
| /*! | ||||
|  * \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 @@ static int vec_contact_add(void *obj, void *arg, int flags) | ||||
|  * \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 @@ static void remove_excess_contacts(struct ao2_container *contacts, struct ao2_co | ||||
| 	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 @@ static int registrar_add_non_permanent(void *obj, void *arg, int flags) | ||||
| 	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 @@ static void register_aor_core(pjsip_rx_data *rdata, | ||||
| 	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 @@ static void register_aor_core(pjsip_rx_data *rdata, | ||||
| 		/* 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 @@ static void register_aor_core(pjsip_rx_data *rdata, | ||||
| 		/* 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); | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user