From 553bb13f82a92f5f75af1e231f4cada5ef3ac15d Mon Sep 17 00:00:00 2001
From: Wenpeng Song <wenpeng.song@iopsys.eu>
Date: Wed, 15 Jan 2025 09:13:49 +0000
Subject: [PATCH] Add max sessions support per client config, REF 12409

Add and support max_sessions to the pjsip endpoint config, which reflect as `VoiceService.
{i}.SIP.Client.{i}.MaxSessions`.

No actual behavior changes have been done, just followed the same behavior and add an extra
condition check after max session per line as the line_calls count was calculated per
endpoint.
---
 include/asterisk/res_pjsip.h        |  1 +
 res/res_pjsip/pjsip_configuration.c |  3 +++
 res/res_pjsip_session.c             | 11 +++++++++++
 3 files changed, 15 insertions(+)

diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 0db9a1917b..02ac1ed7f9 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -1103,6 +1103,7 @@ struct ast_sip_endpoint {
 	unsigned int failover_reg_addr;
 	/* the transport name of latest registration */
 	char *register_transport;
+	unsigned int max_sessions;
 };
 
 struct pjsip_register_dest {
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 8a3cf2a053..e9bba44565 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -47,6 +47,8 @@ struct sip_persistent_endpoint {
 	struct ast_endpoint *endpoint;
 };
 
+#define DEFAULT_MAX_SESSIONS_PER_CLIENT 0
+
 /*! \brief Container for persistent endpoint information */
 static struct ao2_container *persistent_endpoints;
 
@@ -2354,6 +2356,7 @@ int ast_res_pjsip_initialize_configuration(void)
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "security_negotiation", "no", security_negotiation_handler, security_negotiation_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_aoc", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, send_aoc));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "Emergency", "false", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, Emergency));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_sessions", __stringify(DEFAULT_MAX_SESSIONS_PER_CLIENT), OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, max_sessions));
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 11edcd9c07..a643ff6ba1 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -3485,6 +3485,9 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint
 	if (line_calls >= ast_sip_get_max_sessions_per_line()) {
 		ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", line_calls, ast_sip_get_max_sessions_per_line());
 		SCOPE_EXIT_RTN_VALUE(NULL, "Max call per line limit exceeded\n");
+	} else if (endpoint->max_sessions && line_calls >= endpoint->max_sessions) {
+		ast_log(LOG_WARNING, "Max call per client limit exceeded [%d/%d]\n", line_calls, endpoint->max_sessions);
+		SCOPE_EXIT_RTN_VALUE(NULL, "Max call per client limit exceeded\n");
 	} else if (((current_session_count >= ast_sip_get_max_sessions_total()) && (ast_active_calls() != 3)) ||
 		   ((current_session_count >= ast_sip_get_max_sessions_total()) && (ast_active_channels() >= 7))) {
 		ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, ast_sip_get_max_sessions_total());
@@ -4379,6 +4382,14 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
 			pjsip_inv_terminate(inv_session, 486, PJ_FALSE);
 		}
 		SCOPE_EXIT_RTN("Max call per line limit exceeded\n");
+	} else if (endpoint->max_sessions && line_calls >= endpoint->max_sessions) {
+		ast_log(LOG_WARNING, "Max call per client limit exceeded [%d/%d]\n", line_calls, endpoint->max_sessions);
+		/* Dialog's lock and reference are removed in new_invite_initial_answer */
+		if (!new_invite_initial_answer(inv_session, rdata, 486, 486, PJ_FALSE)) {
+			/* Terminate the session if it wasn't done in the answer */
+			pjsip_inv_terminate(inv_session, 486, PJ_FALSE);
+		}
+		SCOPE_EXIT_RTN("Max call per client limit exceeded\n");
 	} else if ((current_session_count == ast_sip_get_max_sessions_total()) || (ast_active_calls() >= ast_sip_get_max_sessions_total())) {
 		ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", current_session_count, ast_sip_get_max_sessions_total());
 		/* Dialog's lock and reference are removed in new_invite_initial_answer */
-- 
GitLab