diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 5d0c02926b..2e41094edd 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -216,14 +216,16 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *b \return SWITCH_STATUS_SUCCESS if the operation was a success */ SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start, - switch_port_t end, uint8_t inc, switch_core_port_allocator_t **new_allocator); + switch_port_t end, switch_port_flag_t flags, switch_core_port_allocator_t **new_allocator); /*! \brief Get a port from the port allocator \param alloc the allocator object - \return the port + \param port a pointer to the port + \return SUCCESS */ -SWITCH_DECLARE(switch_port_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc); +SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc, switch_port_t *port_ptr); +SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_free_port(switch_core_port_allocator_t *alloc, switch_port_t port); /*! \brief destroythe port allocator diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index 896c11bdb4..67e0f324d3 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -53,6 +53,7 @@ typedef void (*switch_rtp_invalid_handler_t) (switch_rtp_t *rtp_session, \note Generally called by the core_init */ SWITCH_DECLARE(void) switch_rtp_init(switch_memory_pool_t *pool); +SWITCH_DECLARE(void) switch_rtp_shutdown(void); /*! \brief Set/Get RTP start port @@ -70,9 +71,10 @@ SWITCH_DECLARE(switch_port_t) switch_rtp_set_end_port(switch_port_t port); /*! \brief Request a new port to be used for media + \param ip the ip to request a port from \return the new port to use */ -SWITCH_DECLARE(switch_port_t) switch_rtp_request_port(void); +SWITCH_DECLARE(switch_port_t) switch_rtp_request_port(const char *ip); /*! \brief create a new RTP session handle diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 76067565f2..03c4066886 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -136,6 +136,12 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_BITS_PER_BYTE 8 typedef uint8_t switch_byte_t; +typedef enum { + SPF_NONE = 0, + SPF_ODD = (1 << 0), + SPF_EVEN = (1 << 1) +} switch_port_flag_t; + typedef enum { ED_MUX_READ = (1 << 0), ED_MUX_WRITE = (1 << 1), diff --git a/src/mod/applications/mod_esf/mod_esf.c b/src/mod/applications/mod_esf/mod_esf.c index 43d9b8d7dc..337c2434ea 100644 --- a/src/mod/applications/mod_esf/mod_esf.c +++ b/src/mod/applications/mod_esf/mod_esf.c @@ -153,7 +153,10 @@ SWITCH_STANDARD_APP(bcast_function) } if (ready == SEND_TYPE_RTP) { - rtp_port = switch_rtp_request_port(); + if (!(rtp_port = switch_rtp_request_port(guess_ip))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP Port Error\n"); + goto fail; + } switch_find_local_ip(guess_ip, sizeof(guess_ip), AF_INET); rtp_session = switch_rtp_new(guess_ip, rtp_port, diff --git a/src/mod/endpoints/mod_dingaling/mod_dingaling.c b/src/mod/endpoints/mod_dingaling/mod_dingaling.c index 983ef7f077..93caada71b 100644 --- a/src/mod/endpoints/mod_dingaling/mod_dingaling.c +++ b/src/mod/endpoints/mod_dingaling/mod_dingaling.c @@ -1697,7 +1697,11 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi switch_core_session_set_private(*new_session, tech_pvt); tech_pvt->session = *new_session; tech_pvt->codec_index = -1; - tech_pvt->local_port = switch_rtp_request_port(); + if (!(tech_pvt->local_port = switch_rtp_request_port(tech_pvt->profile->ip))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No RTP port available!\n"); + terminate_session(new_session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + } tech_pvt->recip = switch_core_session_strdup(*new_session, full_id); tech_pvt->dnis = switch_core_session_strdup(*new_session, dnis); } else { @@ -2584,7 +2588,12 @@ static ldl_status handle_signalling(ldl_handle_t * handle, ldl_session_t * dlses tech_pvt->session = session; tech_pvt->codec_index = -1; tech_pvt->profile = profile; - tech_pvt->local_port = switch_rtp_request_port(); + if (!(tech_pvt->local_port = switch_rtp_request_port(profile->ip))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No RTP port available!\n"); + terminate_session(&session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + status = LDL_STATUS_FALSE; + goto done; + } switch_set_flag_locked(tech_pvt, TFLAG_ANSWER); tech_pvt->recip = switch_core_session_strdup(session, from); if (!(exten = ldl_session_get_value(dlsession, "dnis"))) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index b56d78d752..3266f8ca05 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -398,7 +398,10 @@ switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt) } tech_pvt->local_sdp_audio_ip = ip; - tech_pvt->local_sdp_audio_port = switch_rtp_request_port(); + if (!(tech_pvt->local_sdp_audio_port = switch_rtp_request_port(tech_pvt->profile->rtpip))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No RTP ports available!\n"); + return SWITCH_STATUS_FALSE; + } sdp_port = tech_pvt->local_sdp_audio_port; if (tech_pvt->profile->extrtpip) { @@ -428,7 +431,10 @@ switch_status_t sofia_glue_tech_choose_video_port(private_object_t *tech_pvt) return SWITCH_STATUS_SUCCESS; } - tech_pvt->local_sdp_video_port = switch_rtp_request_port(); + if (!(tech_pvt->local_sdp_video_port = switch_rtp_request_port(tech_pvt->profile->rtpip))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "No RTP ports available!\n"); + return SWITCH_STATUS_FALSE; + } sdp_port = tech_pvt->local_sdp_video_port; if (tech_pvt->profile->extrtpip) { diff --git a/src/switch_core.c b/src/switch_core.c index b31740261d..a9ecd784d2 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -994,13 +994,12 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) } switch_scheduler_task_thread_stop(); + switch_rtp_shutdown(); switch_xml_destroy(); switch_core_memory_stop(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Finalizing Shutdown.\n"); switch_log_shutdown(); - - if (runtime.console && runtime.console != stdout && runtime.console != stderr) { fclose(runtime.console); runtime.console = NULL; @@ -1028,6 +1027,18 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) return SWITCH_STATUS_SUCCESS; } +SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid, switch_management_action_t action, char *data, switch_size_t datalen) +{ + const switch_management_interface_t *ptr; + switch_status_t status = SWITCH_STATUS_FALSE; + + if ((ptr = switch_loadable_module_get_management_interface(relative_oid))) { + status = ptr->management_function(relative_oid, action, data, datalen); + } + + return status; +} + SWITCH_DECLARE(void) switch_core_memory_reclaim_all(void) { switch_core_memory_reclaim_logger(); diff --git a/src/switch_core_port_allocator.c b/src/switch_core_port_allocator.c index a2170e1b36..7d912529e4 100644 --- a/src/switch_core_port_allocator.c +++ b/src/switch_core_port_allocator.c @@ -38,18 +38,21 @@ struct switch_core_port_allocator { switch_port_t start; switch_port_t end; switch_port_t next; - uint8_t inc; + switch_byte_t *track; + uint32_t track_len; + uint32_t track_used; + switch_port_flag_t flags; switch_mutex_t *mutex; switch_memory_pool_t *pool; }; SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start, - switch_port_t end, uint8_t inc, switch_core_port_allocator_t **new_allocator) + switch_port_t end, switch_port_flag_t flags, switch_core_port_allocator_t **new_allocator) { switch_status_t status; switch_memory_pool_t *pool; switch_core_port_allocator_t *alloc; - + if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) { return status; } @@ -58,13 +61,21 @@ SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t sta switch_core_destroy_memory_pool(&pool); return SWITCH_STATUS_MEMERR; } + + alloc->track_len = (end - start) + 2; + alloc->flags = flags; + + if (!(switch_test_flag(alloc, SPF_EVEN) && switch_test_flag(alloc, SPF_ODD))) { + alloc->track_len /= 2; + } + + alloc->track = switch_core_alloc(pool, (alloc->track_len + 2) * sizeof(switch_byte_t)); alloc->start = start; alloc->next = start; alloc->end = end; - if (!(alloc->inc = inc)) { - alloc->inc = 2; - } + + switch_mutex_init(&alloc->mutex, SWITCH_MUTEX_NESTED, pool); alloc->pool = pool; *new_allocator = alloc; @@ -72,34 +83,89 @@ SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t sta return SWITCH_STATUS_SUCCESS; } -SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid, switch_management_action_t action, char *data, switch_size_t datalen) -{ - const switch_management_interface_t *ptr; - switch_status_t status = SWITCH_STATUS_FALSE; - if ((ptr = switch_loadable_module_get_management_interface(relative_oid))) { - status = ptr->management_function(relative_oid, action, data, datalen); +SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc, switch_port_t *port_ptr) +{ + switch_port_t port = 0; + switch_status_t status = SWITCH_STATUS_FALSE; + int even = switch_test_flag(alloc, SPF_EVEN); + int odd = switch_test_flag(alloc, SPF_ODD); + + switch_mutex_lock(alloc->mutex); + srand(getpid() + time(NULL)); + + while(alloc->track_used < alloc->track_len) { + double r; + int index; + int tries = 0; + + do { + r = ((double)rand() / ((double)(RAND_MAX)+(double)(1))); + index = (int) (r * alloc->track_len); + tries++; + } while((alloc->track[index] || index >= alloc->track_len) && tries < 10000); + + while(alloc->track[index]) { + if (++index >= alloc->track_len) { + index = 0; + } + } + + if (index < alloc->track_len) { + alloc->track[index] = 1; + alloc->track_used++; + status = SWITCH_STATUS_SUCCESS; + + if ((even && odd)) { + port = index + alloc->start; + } else { + port = index + (alloc->start / 2); + port *= 2; + } + goto end; + } } + + + end: + + switch_mutex_unlock(alloc->mutex); + + if (status == SWITCH_STATUS_SUCCESS) { + *port_ptr = port; + } else { + *port_ptr = 0; + } + + + return status; + +} + + +SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_free_port(switch_core_port_allocator_t *alloc, switch_port_t port) +{ + switch_status_t status = SWITCH_STATUS_FALSE; + int even = switch_test_flag(alloc, SPF_EVEN); + int odd = switch_test_flag(alloc, SPF_ODD); + int index = port - alloc->start; + + if (!(even && odd)) { + index /= 2; + } + + switch_mutex_lock(alloc->mutex); + if (alloc->track[index]) { + alloc->track[index] = 0; + alloc->track_used--; + status = SWITCH_STATUS_SUCCESS; + } + switch_mutex_unlock(alloc->mutex); return status; } - -SWITCH_DECLARE(switch_port_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc) -{ - switch_port_t port; - - switch_mutex_lock(alloc->mutex); - port = alloc->next; - alloc->next = alloc->next + alloc->inc; - if (alloc->next > alloc->end) { - alloc->next = alloc->start; - } - switch_mutex_unlock(alloc->mutex); - return port; -} - SWITCH_DECLARE(void) switch_core_port_allocator_destroy(switch_core_port_allocator_t **alloc) { switch_memory_pool_t *pool = (*alloc)->pool; diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 9663fbcb3c..69be118cda 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -67,7 +67,7 @@ typedef srtp_hdr_t rtp_hdr_t; #pragma pack() #endif - +static switch_hash_t *alloc_hash = NULL; typedef struct { srtp_hdr_t header; @@ -147,7 +147,8 @@ struct switch_rtp { uint32_t flags; switch_memory_pool_t *pool; switch_sockaddr_t *from_addr; - + char *rx_host; + switch_port_t rx_port; char *ice_user; char *user_ice; char *timer_name; @@ -261,12 +262,17 @@ SWITCH_DECLARE(void) switch_rtp_init(switch_memory_pool_t *pool) if (global_init) { return; } - + switch_core_hash_init(&alloc_hash, pool); srtp_init(); switch_mutex_init(&port_lock, SWITCH_MUTEX_NESTED, pool); global_init = 1; } +SWITCH_DECLARE(void) switch_rtp_shutdown(void) +{ + switch_core_hash_destroy(&alloc_hash); +} + SWITCH_DECLARE(switch_port_t) switch_rtp_set_start_port(switch_port_t port) { if (port) { @@ -304,16 +310,43 @@ SWITCH_DECLARE(switch_port_t) switch_rtp_set_end_port(switch_port_t port) return END_PORT; } -SWITCH_DECLARE(switch_port_t) switch_rtp_request_port(void) +static void release_port(switch_rtp_t *rtp_session) { - switch_port_t port; + switch_core_port_allocator_t *alloc = NULL; - switch_mutex_lock(port_lock); - port = NEXT_PORT; - NEXT_PORT += 2; - if (NEXT_PORT > END_PORT) { - NEXT_PORT = START_PORT; + if (!rtp_session->rx_host) { + return; } + + switch_mutex_lock(port_lock); + if ((alloc = switch_core_hash_find(alloc_hash, rtp_session->rx_host))) { + switch_core_port_allocator_free_port(alloc, rtp_session->rx_port); + } + switch_mutex_unlock(port_lock); + +} + +SWITCH_DECLARE(switch_port_t) switch_rtp_request_port(const char *ip) +{ + switch_port_t port = 0; + switch_core_port_allocator_t *alloc = NULL; + + switch_mutex_lock(port_lock); + alloc = switch_core_hash_find(alloc_hash, ip); + if (!alloc) { + if (switch_core_port_allocator_new(START_PORT, END_PORT, SPF_EVEN, &alloc) != SWITCH_STATUS_SUCCESS) { + port = 0; + goto end; + } + + switch_core_hash_insert(alloc_hash, ip, alloc); + } + + if (switch_core_port_allocator_request_port(alloc, &port) != SWITCH_STATUS_SUCCESS) { + port = 0; + } + + end: switch_mutex_unlock(port_lock); return port; } @@ -379,7 +412,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s *err = "Send myself a packet failed!"; goto done; } - + release_port(rtp_session); + old_sock = rtp_session->sock; rtp_session->sock = new_sock; new_sock = NULL; @@ -395,7 +429,9 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s done: - if (status != SWITCH_STATUS_SUCCESS) { + if (status == SWITCH_STATUS_SUCCESS) { + rtp_session->rx_host = switch_core_strdup(rtp_session->pool, host); + rtp_session->rx_port = port; rtp_session->ready = 1; } @@ -653,6 +689,7 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session) return; } + (*rtp_session)->ready = 0; switch_mutex_lock((*rtp_session)->flag_mutex); @@ -683,6 +720,8 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session) switch_core_timer_destroy(&(*rtp_session)->timer); } + release_port(*rtp_session); + switch_mutex_unlock((*rtp_session)->flag_mutex); return; }