From 87d48dd016294375069a4e6d89e2c7a4097d8264 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 18 Jul 2008 16:18:31 +0000 Subject: [PATCH] update git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@9088 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/include/switch_rtp.h | 2 + src/mod/endpoints/mod_sofia/mod_sofia.h | 14 ++- src/mod/endpoints/mod_sofia/sofia.c | 14 ++- src/mod/endpoints/mod_sofia/sofia_glue.c | 59 +++++++++++-- src/switch_rtp.c | 105 +++++++++++++++++++++-- src/switch_stun.c | 26 +++++- 6 files changed, 200 insertions(+), 20 deletions(-) diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index 8b53de6c17..7b017edab6 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -418,6 +418,8 @@ SWITCH_DECLARE(void) switch_rtp_set_cng_pt(switch_rtp_t *rtp_session, switch_pay */ SWITCH_DECLARE(void *) switch_rtp_get_private(switch_rtp_t *rtp_session); +SWITCH_DECLARE(switch_status_t) switch_rtp_activate_stun_ping(switch_rtp_t *rtp_session, const char *stun_ip, uint32_t packet_count, switch_bool_t funny); + /*! \} */ diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 1228671ab7..024b9f6077 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -156,7 +156,8 @@ typedef enum { PFLAG_RECIEVED_IN_NAT_REG_CONTACT = (1 << 22), PFLAG_3PCC = (1 << 23), PFLAG_DISABLE_RTP_AUTOADJ = (1 << 24), - PFLAG_DISABLE_SRTP_AUTH = (1 << 25) + PFLAG_DISABLE_SRTP_AUTH = (1 << 25), + PFLAG_FUNNY_STUN = (1 << 26) } PFLAGS; typedef enum { @@ -164,6 +165,12 @@ typedef enum { PFLAG_NDLB_BROKEN_AUTH_HASH = (1 << 1) } sofia_NDLB_t; +typedef enum { + STUN_FLAG_SET = (1 << 0), + STUN_FLAG_PING = (1 << 1), + STUN_FLAG_FUNNY = (1 << 2) +} STUNFLAGS; + typedef enum { TFLAG_IO = (1 << 0), TFLAG_CHANGE_MEDIA = (1 << 1), @@ -417,6 +424,9 @@ struct private_object { char *local_crypto_key; char *remote_crypto_key; char *record_route; + char *extrtpip; + char *stun_ip; + uint32_t stun_flags; int crypto_tag; unsigned char local_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN]; unsigned char remote_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN]; @@ -566,7 +576,7 @@ void sofia_glue_actually_execute_sql(sofia_profile_t *profile, switch_bool_t mas void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot); void sofia_reg_check_gateway(sofia_profile_t *profile, time_t now); void sofia_reg_unregister(sofia_profile_t *profile); -switch_status_t sofia_glue_ext_address_lookup(char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool); +switch_status_t sofia_glue_ext_address_lookup(sofia_profile_t *profile, private_object_t *tech_pvt, char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool); void sofia_glue_pass_sdp(private_object_t *tech_pvt, char *sdp); int sofia_glue_get_user_host(char *in, char **user, char **host); diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 9e99fefa6c..61636a7919 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1047,6 +1047,12 @@ switch_status_t reconfig_sofia(sofia_profile_t *profile) } else { profile->pflags &= ~PFLAG_DISABLE_SRTP_AUTH; } + } else if (!strcasecmp(var, "NDLB-funny-stun")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_FUNNY_STUN; + } else { + profile->pflags &= ~PFLAG_FUNNY_STUN; + } } else if (!strcasecmp(var, "rfc2833-pt")) { profile->te = (switch_payload_t) atoi(val); } else if (!strcasecmp(var, "cng-pt")) { @@ -1380,6 +1386,12 @@ switch_status_t config_sofia(int reload, char *profile_name) profile->pflags |= PFLAG_DISABLE_RTP_AUTOADJ; } else if (!strcasecmp(var, "NDLB-support-asterisk-missing-srtp-auth") && switch_true(val)) { profile->pflags |= PFLAG_DISABLE_SRTP_AUTH; + } else if (!strcasecmp(var, "NDLB-funny-stun")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_FUNNY_STUN; + } else { + profile->pflags &= ~PFLAG_FUNNY_STUN; + } } else if (!strcasecmp(var, "rfc2833-pt")) { profile->te = (switch_payload_t) atoi(val); } else if (!strcasecmp(var, "cng-pt")) { @@ -1435,7 +1447,7 @@ switch_status_t config_sofia(int reload, char *profile_name) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invald IP 0.0.0.0 replaced with %s\n", mod_sofia_globals.guess_ip); } else if (strcasecmp(val, "auto")) { switch_port_t port = 0; - if (sofia_glue_ext_address_lookup(&myip, &port, val, profile->pool) == SWITCH_STATUS_SUCCESS) { + if (sofia_glue_ext_address_lookup(profile, NULL, &myip, &port, val, profile->pool) == SWITCH_STATUS_SUCCESS) { ip = myip; } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to get external ip.\n"); diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index e682575b3f..aa110af765 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -425,11 +425,14 @@ void sofia_glue_attach_private(switch_core_session_t *session, sofia_profile_t * switch_channel_set_name(tech_pvt->channel, name); } -switch_status_t sofia_glue_ext_address_lookup(char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool) +switch_status_t sofia_glue_ext_address_lookup(sofia_profile_t *profile, private_object_t *tech_pvt, char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool) { char *error = ""; switch_status_t status = SWITCH_STATUS_FALSE; int x; + switch_port_t myport = *port; + const char *var; + int funny = 0; if (!sourceip) { return status; @@ -442,6 +445,11 @@ switch_status_t sofia_glue_ext_address_lookup(char **ip, switch_port_t *port, ch return status; } for (x = 0; x < 5; x++) { + if ((profile->pflags & PFLAG_FUNNY_STUN) || + (tech_pvt && (var = switch_channel_get_variable(tech_pvt->channel, "funny_stun")) && switch_true(var))) { + error = "funny"; + funny++; + } if ((status = switch_stun_lookup(ip, port, stun_ip, SWITCH_STUN_DEFAULT_PORT, &error, pool)) != SWITCH_STATUS_SUCCESS) { switch_yield(100000); } else { @@ -456,7 +464,18 @@ switch_status_t sofia_glue_ext_address_lookup(char **ip, switch_port_t *port, ch switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! No IP returned\n"); return SWITCH_STATUS_FALSE; } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Stun Success [%s]:[%d]\n", *ip, *port); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Stun Success [%s]:[%d] [%s][%d]\n", *ip, *port, tech_pvt->profile->rtpip, myport); + if (tech_pvt) { + if (myport == *port && !strcmp(*ip, tech_pvt->profile->rtpip)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Stun Not Required ip and port match. [%s]:[%d]\n", *ip, *port); + } else { + tech_pvt->stun_ip = switch_core_session_strdup(tech_pvt->session, stun_ip); + tech_pvt->stun_flags |= STUN_FLAG_SET; + if (funny) { + tech_pvt->stun_flags |= STUN_FLAG_FUNNY; + } + } + } } else { *ip = sourceip; } @@ -482,6 +501,7 @@ switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt, int forc char *ip = tech_pvt->profile->rtpip; switch_port_t sdp_port; char tmp[50]; + const char *use_ip = NULL; if (!force) { if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE) || @@ -502,12 +522,19 @@ switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt, int forc } sdp_port = tech_pvt->local_sdp_audio_port; - if (tech_pvt->profile->extrtpip) { - if (sofia_glue_ext_address_lookup(&ip, &sdp_port, tech_pvt->profile->extrtpip, switch_core_session_get_pool(tech_pvt->session)) != - SWITCH_STATUS_SUCCESS) { + if (!(use_ip = switch_channel_get_variable(tech_pvt->channel, "rtp_adv_audio_ip"))) { + if (tech_pvt->profile->extrtpip) { + use_ip = tech_pvt->profile->extrtpip; + } + } + + if (use_ip) { + tech_pvt->extrtpip = switch_core_session_strdup(tech_pvt->session, use_ip); + if (sofia_glue_ext_address_lookup(tech_pvt->profile, tech_pvt, &ip, &sdp_port, tech_pvt->extrtpip, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; } } + tech_pvt->adv_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, ip); tech_pvt->adv_sdp_audio_port = sdp_port; @@ -544,7 +571,7 @@ switch_status_t sofia_glue_tech_choose_video_port(private_object_t *tech_pvt, in if (tech_pvt->profile->extrtpip) { - if (sofia_glue_ext_address_lookup(&ip, &sdp_port, tech_pvt->profile->extrtpip, switch_core_session_get_pool(tech_pvt->session)) != + if (sofia_glue_ext_address_lookup(tech_pvt->profile, tech_pvt, &ip, &sdp_port, tech_pvt->profile->extrtpip, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; } @@ -1730,7 +1757,8 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f uint8_t vad_in = switch_test_flag(tech_pvt, TFLAG_VAD_IN) ? 1 : 0; uint8_t vad_out = switch_test_flag(tech_pvt, TFLAG_VAD_OUT) ? 1 : 0; uint8_t inb = switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? 0 : 1; - + uint32_t stun_ping = 0; + if ((val = switch_channel_get_variable(tech_pvt->channel, "rtp_enable_vad_in")) && switch_true(val)) { vad_in = 1; } @@ -1745,6 +1773,18 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f vad_out = 0; } + if ((tech_pvt->stun_flags & STUN_FLAG_SET) && (val = switch_channel_get_variable(tech_pvt->channel, "rtp_stun_ping"))) { + int ival = atoi(val); + + if (ival <= 0) { + if (switch_true(val)) { + ival = 6; + } + } + + stun_ping = (ival * tech_pvt->read_codec.implementation->samples_per_second) / tech_pvt->read_codec.implementation->samples_per_frame; + } + tech_pvt->ssrc = switch_rtp_get_ssrc(tech_pvt->rtp_session); switch_set_flag(tech_pvt, TFLAG_RTP); switch_set_flag(tech_pvt, TFLAG_IO); @@ -1756,6 +1796,11 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f switch_channel_get_name(switch_core_session_get_channel(tech_pvt->session)), vad_in ? "in" : "", vad_out ? "out" : ""); } + if (stun_ping) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Setting stun ping to %s:%d\n", tech_pvt->stun_ip, stun_ping); + switch_rtp_activate_stun_ping(tech_pvt->rtp_session, tech_pvt->stun_ip, stun_ping, (tech_pvt->stun_flags & STUN_FLAG_FUNNY) ? 1 : 0); + } + if ((val = switch_channel_get_variable(tech_pvt->channel, "jitterbuffer_msec"))) { int len = atoi(val); diff --git a/src/switch_rtp.c b/src/switch_rtp.c index a9c6de0361..6243d75110 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -135,6 +135,9 @@ struct switch_rtp { switch_sockaddr_t *remote_addr; rtp_msg_t recv_msg; + + switch_sockaddr_t *remote_stun_addr; + uint32_t autoadj_window; uint32_t autoadj_tally; @@ -169,7 +172,9 @@ struct switch_rtp { uint32_t rsamples_per_interval; uint32_t ms_per_packet; uint32_t remote_port; - uint8_t stuncount; + uint32_t stuncount; + uint32_t funny_stun; + uint32_t default_stuncount; struct switch_rtp_vad_data vad_data; struct switch_rtp_rfc2833_data dtmf_data; switch_payload_t te; @@ -188,6 +193,7 @@ struct switch_rtp { switch_rtp_crypto_key_t *crypto_keys[SWITCH_RTP_CRYPTO_MAX]; int reading; int writing; + char *stun_ip; }; static int global_init = 0; @@ -195,6 +201,52 @@ static int rtp_common_write(switch_rtp_t *rtp_session, rtp_msg_t *send_msg, void *data, uint32_t datalen, switch_payload_t payload, uint32_t timestamp, switch_frame_flag_t *flags); +static switch_status_t do_stun_ping(switch_rtp_t *rtp_session) +{ + uint8_t buf[256] = { 0 }; + uint8_t *start = buf; + switch_stun_packet_t *packet; + unsigned int elapsed; + switch_size_t bytes; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + switch_assert(rtp_session != NULL); + + WRITE_INC(rtp_session); + + if (rtp_session->stuncount != 0) { + rtp_session->stuncount--; + goto end; + } + + if (rtp_session->last_stun) { + elapsed = (unsigned int) ((switch_timestamp_now() - rtp_session->last_stun) / 1000); + + if (elapsed > 30000) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No stun for a long time (PUNT!)\n"); + status = SWITCH_STATUS_FALSE; + goto end; + } + } + + if (rtp_session->funny_stun) { + *start++ = 0; + *start++ = 0; + *start++ = 0x22; + *start++ = 0x22; + } + + packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_REQUEST, NULL, start); + bytes = switch_stun_packet_length(packet); + switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_stun_addr, 0, (void *) packet, &bytes); + rtp_session->stuncount = rtp_session->default_stuncount; + + end: + WRITE_DEC(rtp_session); + + return status; +} + static switch_status_t ice_out(switch_rtp_t *rtp_session) { uint8_t buf[256] = { 0 }; @@ -214,7 +266,7 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session) } if (rtp_session->last_stun) { - elapsed = (unsigned int) ((switch_time_now() - rtp_session->last_stun) / 1000); + elapsed = (unsigned int) ((switch_timestamp_now() - rtp_session->last_stun) / 1000); if (elapsed > 30000) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No stun for a long time (PUNT!)\n"); @@ -227,12 +279,22 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session) switch_stun_packet_attribute_add_username(packet, rtp_session->ice_user, 32); bytes = switch_stun_packet_length(packet); switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, (void *) packet, &bytes); - rtp_session->stuncount = 25; + rtp_session->stuncount = rtp_session->default_stuncount; end: WRITE_DEC(rtp_session); - return SWITCH_STATUS_SUCCESS; + return status; +} + + +static void handle_stun_ping_reply(switch_rtp_t *rtp_session, void *data, switch_size_t len) +{ + if (!switch_rtp_ready(rtp_session)) { + return; + } + + rtp_session->last_stun = switch_timestamp_now(); } static void handle_ice(switch_rtp_t *rtp_session, void *data, switch_size_t len) @@ -268,7 +330,7 @@ static void handle_ice(switch_rtp_t *rtp_session, void *data, switch_size_t len) } end_buf = buf + ((sizeof(buf) > packet->header.length) ? packet->header.length : sizeof(buf)); - rtp_session->last_stun = switch_time_now(); + rtp_session->last_stun = switch_timestamp_now(); switch_stun_packet_first_attribute(packet, attr); @@ -828,6 +890,26 @@ SWITCH_DECLARE(void) switch_rtp_set_cng_pt(switch_rtp_t *rtp_session, switch_pay rtp_session->cng_pt = pt; } +SWITCH_DECLARE(switch_status_t) switch_rtp_activate_stun_ping(switch_rtp_t *rtp_session, const char *stun_ip, uint32_t packet_count, switch_bool_t funny) +{ + + if (switch_sockaddr_info_get(&rtp_session->remote_stun_addr, stun_ip, SWITCH_UNSPEC, + SWITCH_STUN_DEFAULT_PORT, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS || !rtp_session->remote_stun_addr) { + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error resolving stun ping addr\n"); + return SWITCH_STATUS_FALSE; + } + + if (funny) { + rtp_session->funny_stun++; + } + + rtp_session->default_stuncount = packet_count; + + rtp_session->stun_ip = switch_core_strdup(rtp_session->pool, stun_ip); + return SWITCH_STATUS_SUCCESS; +} + SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t *rtp_session, uint32_t queue_frames) { rtp_session->jb = stfu_n_init(queue_frames); @@ -843,6 +925,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_ice(switch_rtp_t *rtp_sessio switch_snprintf(user_ice, sizeof(user_ice), "%s%s", rlogin, login); rtp_session->ice_user = switch_core_strdup(rtp_session->pool, ice_user); rtp_session->user_ice = switch_core_strdup(rtp_session->pool, user_ice); + rtp_session->default_stuncount = 25; if (rtp_session->ice_user) { if (ice_out(rtp_session) != SWITCH_STATUS_SUCCESS) { @@ -1218,8 +1301,12 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (bytes && rtp_session->recv_msg.header.version != 2) { uint8_t *data = (uint8_t *) rtp_session->recv_msg.body; - if (rtp_session->recv_msg.header.version == 0 && rtp_session->ice_user) { - handle_ice(rtp_session, (void *) &rtp_session->recv_msg, bytes); + if (rtp_session->recv_msg.header.version == 0) { + if (rtp_session->ice_user) { + handle_ice(rtp_session, (void *) &rtp_session->recv_msg, bytes); + } else if (rtp_session->remote_stun_addr) { + handle_stun_ping_reply(rtp_session, (void *) &rtp_session->recv_msg, bytes); + } } if (rtp_session->invalid_handler) { @@ -1834,6 +1921,10 @@ static int rtp_common_write(switch_rtp_t *rtp_session, rtp_session->last_write_ts = this_ts; } + if (rtp_session->remote_stun_addr) { + do_stun_ping(rtp_session); + } + if (rtp_session->ice_user) { if (ice_out(rtp_session) != SWITCH_STATUS_SUCCESS) { ret = -1; diff --git a/src/switch_stun.c b/src/switch_stun.c index e85ac236cc..5ed6b6862d 100644 --- a/src/switch_stun.c +++ b/src/switch_stun.c @@ -433,7 +433,8 @@ SWITCH_DECLARE(switch_status_t) switch_stun_lookup(char **ip, { switch_sockaddr_t *local_addr = NULL, *remote_addr = NULL, *from_addr = NULL; switch_socket_t *sock = NULL; - uint8_t buf[256] = { 0 }; + uint8_t buf[260] = { 0 }; + uint8_t *start = buf; void *end_buf; switch_stun_packet_t *packet; switch_stun_packet_attribute_t *attr; @@ -443,6 +444,14 @@ SWITCH_DECLARE(switch_status_t) switch_stun_lookup(char **ip, uint16_t rport = 0; switch_time_t started = 0; unsigned int elapsed = 0; + int funny = 0; + int size = sizeof(buf); + + switch_assert(err); + + if (!strcmp(*err, "funny")) { + funny = 1; + } *err = "Success"; @@ -468,8 +477,15 @@ SWITCH_DECLARE(switch_status_t) switch_stun_lookup(char **ip, return SWITCH_STATUS_FALSE; } + if (funny) { + *start++ = 0; + *start++ = 0; + *start++ = 0x22; + *start++ = 0x22; + } + switch_socket_opt_set(sock, SWITCH_SO_NONBLOCK, TRUE); - packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_REQUEST, NULL, buf); + packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_REQUEST, NULL, start); switch_stun_random_string(username, 32, NULL); switch_stun_packet_attribute_add_username(packet, username, 32); bytes = switch_stun_packet_length(packet); @@ -496,7 +512,11 @@ SWITCH_DECLARE(switch_status_t) switch_stun_lookup(char **ip, } switch_socket_close(sock); - packet = switch_stun_packet_parse(buf, sizeof(buf)); + if (funny) { + size -= 4; + } + + packet = switch_stun_packet_parse(start, size); if (!packet) { *err = "Invalid STUN/ICE packet"; return SWITCH_STATUS_FALSE;