diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index f4e361318c..c15cbdc14d 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -43,6 +43,8 @@ #define SQL_CACHE_TIMEOUT 300 #define DEFAULT_NONCE_TTL 60 #define IREG_SECONDS 30 +#define IPING_SECONDS 30 +#define IPING_FREQUENCY 1 #define GATEWAY_SECONDS 1 #define SOFIA_QUEUE_SIZE 50000 #define HAVE_APR @@ -720,6 +722,8 @@ struct sofia_profile { uint32_t sip_expires_late_margin; uint32_t sip_subscription_max_deviation; int ireg_seconds; + int iping_seconds; + int iping_freq; sofia_paid_type_t paid_type; uint32_t rtp_digit_delay; switch_queue_t *event_queue; @@ -976,6 +980,7 @@ void sofia_glue_actually_execute_sql_trans(sofia_profile_t *profile, char *sql, void sofia_glue_execute_sql_now(sofia_profile_t *profile, char **sqlp, switch_bool_t sql_already_dynamic); void sofia_glue_execute_sql_soon(sofia_profile_t *profile, char **sqlp, switch_bool_t sql_already_dynamic); void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot); +void sofia_reg_check_ping_expire(sofia_profile_t *profile, time_t now, int interval); void sofia_reg_check_gateway(sofia_profile_t *profile, time_t now); void sofia_sub_check_gateway(sofia_profile_t *profile, time_t now); void sofia_reg_unregister(sofia_profile_t *profile); diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 3c92207ebe..054b656db6 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -2574,6 +2574,7 @@ void *SWITCH_THREAD_FUNC sofia_profile_worker_thread_run(switch_thread_t *thread { sofia_profile_t *profile = (sofia_profile_t *) obj; uint32_t ireg_loops = profile->ireg_seconds; /* Number of loop iterations done when we haven't checked for registrations */ + uint32_t iping_loops = profile->iping_freq; /* Number of loop iterations done when we haven't checked for ping expires */ uint32_t gateway_loops = GATEWAY_SECONDS; /* Number of loop iterations done when we haven't checked for gateways */ sofia_set_pflag_locked(profile, PFLAG_WORKER_RUNNING); @@ -2618,6 +2619,12 @@ void *SWITCH_THREAD_FUNC sofia_profile_worker_thread_run(switch_thread_t *thread sofia_reg_check_expire(profile, now, 0); ireg_loops = 0; } + + if(++iping_loops >= (uint32_t)profile->iping_freq) { + time_t now = switch_epoch_time_now(NULL); + sofia_reg_check_ping_expire(profile, now, profile->iping_seconds); + iping_loops = 0; + } if (++gateway_loops >= GATEWAY_SECONDS) { sofia_reg_check_gateway(profile, switch_epoch_time_now(NULL)); @@ -4099,6 +4106,8 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) profile->mndlb |= SM_NDLB_ALLOW_NONDUP_SDP; profile->te = 101; profile->ireg_seconds = IREG_SECONDS; + profile->iping_seconds = IPING_SECONDS; + profile->iping_freq = IPING_FREQUENCY; profile->paid_type = PAID_DEFAULT; profile->bind_attempts = 2; profile->bind_attempt_interval = 5; @@ -4229,6 +4238,16 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) if (profile->ireg_seconds < 0) { profile->ireg_seconds = IREG_SECONDS; } + } else if (!strcasecmp(var, "ping-mean-interval")) { + profile->iping_seconds = atoi(val); + if (profile->iping_seconds < 0) { + profile->iping_seconds = IPING_SECONDS; + } + } else if (!strcasecmp(var, "ping-thread-frequency")) { + profile->iping_freq = atoi(val); + if (profile->iping_freq < 0) { + profile->iping_freq = IPING_FREQUENCY; + } } else if (!strcasecmp(var, "user-agent-string")) { profile->user_agent = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "auto-restart")) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 0773e45b50..3ed3e42e14 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -1993,6 +1993,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile) " ping_count INTEGER,\n" " rpid VARCHAR(255),\n" " expires BIGINT,\n" + " ping_expires INTEGER not null default 0,\n" " user_agent VARCHAR(255),\n" " server_user VARCHAR(255),\n" " server_host VARCHAR(255),\n" @@ -2121,6 +2122,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile) "create index sr_presence_hosts on sip_registrations (presence_hosts)", "create index sr_contact on sip_registrations (contact)", "create index sr_expires on sip_registrations (expires)", + "create index sr_ping_expires on sip_registrations (ping_expires)", "create index sr_hostname on sip_registrations (hostname)", "create index sr_status on sip_registrations (status)", "create index sr_ping_status on sip_registrations (ping_status)", @@ -2205,6 +2207,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile) switch_cache_db_test_reactive(dbh, "select ping_count from sip_registrations", NULL, "alter table sip_registrations add column ping_count INTEGER default 0"); switch_cache_db_test_reactive(dbh, "select ping_status from sip_registrations", NULL, "alter table sip_registrations add column ping_status VARCHAR(255) default \"Reachable\""); + switch_cache_db_test_reactive(dbh, "select ping_expires from sip_registrations", NULL, "alter table sip_registrations add column ping_expires INTEGER not null default 0"); test2 = switch_mprintf("%s;%s", test_sql, test_sql); diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index d4177195d1..108aaf54d1 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -846,21 +846,45 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); +} + +long sofia_reg_uniform_distribution(int max) +{ +/* + * Generate a random number following a uniform distribution between 0 and max + */ + int result; + int range = max + 1; + + srand((unsigned) switch_thread_self() + switch_micro_time_now()); + result = (int)((double)rand() / (((double)RAND_MAX + (double)1) / range)); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9, "Generated random %ld, max is %d\n", (long) result, max); + return (long) result; +} + +void sofia_reg_check_ping_expire(sofia_profile_t *profile, time_t now, int interval) +{ + char *sql; + int mean = interval / 2; + long next, irand; if (now) { if (sofia_test_pflag(profile, PFLAG_ALL_REG_OPTIONS_PING)) { sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,rpid," "expires,user_agent,server_user,server_host,profile_name" " from sip_registrations where hostname='%s' and " - "profile_name='%s' and orig_hostname='%s'", mod_sofia_globals.hostname, profile->name, mod_sofia_globals.hostname); + "profile_name='%s' and orig_hostname='%s' and " + "ping_expires > 0 and ping_expires <= %ld", mod_sofia_globals.hostname, profile->name, mod_sofia_globals.hostname, (long) now); sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_reg_nat_callback, profile); switch_safe_free(sql); } else if (sofia_test_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING)) { - sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,rpid," - "expires,user_agent,server_user,server_host,profile_name" - " from sip_registrations where status like '%%UDP-NAT%%' " - "and hostname='%s' and profile_name='%s'", mod_sofia_globals.hostname, profile->name); + sql = switch_mprintf( " select call_id,sip_user,sip_host,contact,status,rpid, " + " expires,user_agent,server_user,server_host,profile_name " + " from sip_registrations where status like '%%UDP-NAT%%' " + " and hostname='%s' and profile_name='%s' and ping_expires > 0 and ping_expires <= %ld ", + mod_sofia_globals.hostname, profile->name, (long) now); sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_reg_nat_callback, profile); switch_safe_free(sql); @@ -869,11 +893,37 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) "expires,user_agent,server_user,server_host,profile_name" " from sip_registrations where (status like '%%NAT%%' " "or contact like '%%fs_nat=yes%%') and hostname='%s' " - "and profile_name='%s' and orig_hostname='%s'", mod_sofia_globals.hostname, profile->name, mod_sofia_globals.hostname); + "and profile_name='%s' and orig_hostname='%s' and " + "ping_expires > 0 and ping_expires <= %ld", mod_sofia_globals.hostname, profile->name, mod_sofia_globals.hostname, (long) now); sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_reg_nat_callback, profile); switch_safe_free(sql); } + + if (sofia_test_pflag(profile, PFLAG_ALL_REG_OPTIONS_PING) || + sofia_test_pflag(profile, PFLAG_UDP_NAT_OPTIONS_PING) || + sofia_test_pflag(profile, PFLAG_NAT_OPTIONS_PING)) { + char buf[32] = ""; + int count; + + sql = switch_mprintf("select count(*) from sip_registrations where hostname='%q' and profile_name='%q' and ping_expires <= %ld", + mod_sofia_globals.hostname, profile->name, (long) now); + + sofia_glue_execute_sql2str(profile, profile->dbh_mutex, sql, buf, sizeof(buf)); + switch_safe_free(sql); + count = atoi(buf); + + /* only update if needed */ + if (count) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG9, "Updating ping expires for profile %s\n", profile->name); + irand = mean + sofia_reg_uniform_distribution(interval); + next = (long) now + irand; + + sql = switch_mprintf(" update sip_registrations set ping_expires = %ld where ping_expires <= %ld ", + next, (long) now); + sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE); + } + } } }