diff --git a/CHANGES b/CHANGES index 3cc1cd4f59..622973c48f 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,19 @@ res_pjsip preferred codec rather than advertising all joint codec capabilities. This limits the other side's codec choice to exactly what we prefer. +------------------------------------------------------------------------------ +--- Functionality changes from Asterisk 14.1.0 to Asterisk 14.2.0 ---------- +------------------------------------------------------------------------------ + +res_pjsip +------------------ + * Automatic dual stack support is now implemented. Depending on DNS resolution + and the transport used for sending a message the SIP signaling and SDP will + be updated with the correct IP address and protocol version. This means that + the rtp_ipv6 and t38_udptl_ipv6 options no longer have any effect. The + res_pjsip_multihomed module has also been moved into core res_pjsip to ensure + that messages are updated with the correct address information in all cases. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ---------- ------------------------------------------------------------------------------ diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index c6293b6fbc..3bb9dc5bb7 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -112,9 +112,6 @@ ; the prefix "external_" will only apply to communication with addresses ; outside the range set with "local_net=". ; -; IPv6: For endpoints using IPv6, remember to set "rtp_ipv6=yes" so that the RTP -; engine will also be able to bind to an IPv6 address. -; ; You can have more than one of any type of transport, as long as it doesn't ; use the same resources (bind address, port, etc) as the others. @@ -294,8 +291,6 @@ ; If using the TLS enabled transport, you may want the "media_encryption=sdes" ; option to additionally enable SRTP, though they are not mutually inclusive. ; -; Use the "rtp_ipv6=yes" option if you want to utilize RTP over an ipv6 transport. -; ; If this endpoint were remote, and it was using a transport configured for NAT ; then you likely want to use "direct_media=no" to prevent audio issues. @@ -315,7 +310,6 @@ ;transport=transport-tls ;media_encryption=sdes ;transport=transport-udp-ipv6 -;rtp_ipv6=yes ;transport=transport-udp-nat ;direct_media=no ; @@ -646,7 +640,6 @@ ; must be provided (default: "") ;rewrite_contact=no ; Allow Contact header to be rewritten with the source ; IP address port (default: "no") -;rtp_ipv6=no ; Allow use of IPv6 for RTP traffic (default: "no") ;rtp_symmetric=no ; Enforce that RTP must be symmetric (default: "no") ;send_diversion=yes ; Send the Diversion header conveying the diversion ; information to the called user agent (default: "yes") @@ -699,8 +692,6 @@ ; (default: "0") ;t38_udptl_nat=no ; Whether NAT support is enabled on UDPTL sessions ; (default: "no") -;t38_udptl_ipv6=no ; Whether IPv6 is used for UDPTL Sessions (default: - ; "no") ;tone_zone= ; Set which country s indications to use for channels created ; for this endpoint (default: "") ;language= ; Set the default language to use for channels created for this diff --git a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py index 40e93547d2..98a5e9546a 100755 --- a/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py +++ b/contrib/scripts/sip_to_pjsip/sip_to_pjsip.py @@ -374,7 +374,7 @@ def from_dtlsenable(key, val, section, pjsip, nmapped): ############################################################################### # options in pjsip.conf on an endpoint that have no sip.conf equivalent: -# type, rtp_ipv6, 100rel, trust_id_outbound, aggregate_mwi, +# type, 100rel, trust_id_outbound, aggregate_mwi, # connected_line_method # known sip.conf peer keys that can be mapped to a pjsip.conf section/key diff --git a/res/res_pjsip.c b/res/res_pjsip.c index fa200be26a..39c365aa1a 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -4332,6 +4332,7 @@ static int unload_pjsip(void *data) */ if (ast_pjsip_endpoint && serializer_pool[0]) { ast_res_pjsip_cleanup_options_handling(); + ast_res_pjsip_cleanup_message_ip_updater(); ast_sip_destroy_distributor(); ast_res_pjsip_destroy_configuration(); ast_sip_destroy_system(); @@ -4499,6 +4500,12 @@ static int load_module(void) } ast_res_pjsip_init_options_handling(0); + + if (ast_res_pjsip_init_message_ip_updater()) { + ast_log(LOG_ERROR, "Failed to initialize message IP updating. Aborting load\n"); + goto error; + } + ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands)); AST_TEST_REGISTER(xml_sanitization_end_null); diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h index b175b5e119..0bdb63325a 100644 --- a/res/res_pjsip/include/res_pjsip_private.h +++ b/res/res_pjsip/include/res_pjsip_private.h @@ -183,6 +183,14 @@ void ast_sip_destroy_global_headers(void); */ int ast_res_pjsip_init_options_handling(int reload); +/*! + * \internal Initialize message IP updating handling. + * + * \retval 0 on success + * \retval other on failure + */ +int ast_res_pjsip_init_message_ip_updater(void); + /*! * \internal * \brief Initialize transport storage for contacts. @@ -234,6 +242,12 @@ int ast_sip_initialize_global(void); */ void ast_res_pjsip_cleanup_options_handling(void); +/*! + * \internal + * \brief Clean up res_pjsip message ip updating handling + */ +void ast_res_pjsip_cleanup_message_ip_updater(void); + /*! * \internal * \brief Get threadpool options diff --git a/res/res_pjsip_multihomed.c b/res/res_pjsip/pjsip_message_ip_updater.c similarity index 60% rename from res/res_pjsip_multihomed.c rename to res/res_pjsip/pjsip_message_ip_updater.c index 5deeb92476..7671ad0a75 100644 --- a/res/res_pjsip_multihomed.c +++ b/res/res_pjsip/pjsip_message_ip_updater.c @@ -1,7 +1,7 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2014, Digium, Inc. + * Copyright (C) 2014-2016, Digium, Inc. * * Joshua Colp * @@ -16,19 +16,78 @@ * at the top of the source tree. */ -/*** MODULEINFO - pjproject - res_pjsip - core - ***/ - #include "asterisk.h" #include #include #include "asterisk/res_pjsip.h" -#include "asterisk/module.h" +#include "asterisk/res_pjsip_session.h" +#include "include/res_pjsip_private.h" + +#define MOD_DATA_RESTRICTIONS "restrictions" + +static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata); + +/*! \brief Outgoing message modification restrictions */ +struct multihomed_message_restrictions { + /*! \brief Disallow modification of the From domain */ + unsigned int disallow_from_domain_modification; +}; + +static pjsip_module multihomed_module = { + .name = { "Multihomed Routing", 18 }, + .id = -1, + .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1, + .on_tx_request = multihomed_on_tx_message, + .on_tx_response = multihomed_on_tx_message, +}; + +/*! \brief Helper function to get (or allocate if not already present) restrictions on a message */ +static struct multihomed_message_restrictions *multihomed_get_restrictions(pjsip_tx_data *tdata) +{ + struct multihomed_message_restrictions *restrictions; + + restrictions = ast_sip_mod_data_get(tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS); + if (restrictions) { + return restrictions; + } + + restrictions = PJ_POOL_ALLOC_T(tdata->pool, struct multihomed_message_restrictions); + ast_sip_mod_data_set(tdata->pool, tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS, restrictions); + + return restrictions; +} + +/*! \brief Callback invoked on non-session outgoing messages */ +static void multihomed_outgoing_message(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata) +{ + struct multihomed_message_restrictions *restrictions = multihomed_get_restrictions(tdata); + + restrictions->disallow_from_domain_modification = !ast_strlen_zero(endpoint->fromdomain); +} + +/*! \brief PJSIP Supplement for tagging messages with restrictions */ +static struct ast_sip_supplement multihomed_supplement = { + .priority = AST_SIP_SUPPLEMENT_PRIORITY_FIRST, + .outgoing_request = multihomed_outgoing_message, + .outgoing_response = multihomed_outgoing_message, +}; + +/*! \brief Callback invoked on session outgoing messages */ +static void multihomed_session_outgoing_message(struct ast_sip_session *session, struct pjsip_tx_data *tdata) +{ + struct multihomed_message_restrictions *restrictions = multihomed_get_restrictions(tdata); + + restrictions->disallow_from_domain_modification = !ast_strlen_zero(session->endpoint->fromdomain); +} + +/*! \brief PJSIP Session Supplement for tagging messages with restrictions */ +static struct ast_sip_session_supplement multihomed_session_supplement = { + .priority = 1, + .outgoing_request = multihomed_session_outgoing_message, + .outgoing_response = multihomed_session_outgoing_message, +}; /*! \brief Helper function which returns a UDP transport bound to the given address and port */ static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port) @@ -59,6 +118,21 @@ static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port return sip_transport; } +/*! \brief Helper function which determines if a transport is bound to any */ +static int multihomed_bound_any(pjsip_transport *transport) +{ + pj_uint32_t loop6[4] = {0, 0, 0, 0}; + + if ((transport->local_addr.addr.sa_family == pj_AF_INET() && + transport->local_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) || + (transport->local_addr.addr.sa_family == pj_AF_INET6() && + !pj_memcmp(&transport->local_addr.ipv6.sin6_addr, loop6, sizeof(loop6)))) { + return 1; + } + + return 0; +} + /*! \brief Helper function which determines if the address within SDP should be rewritten */ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp) { @@ -77,26 +151,13 @@ static int multihomed_rewrite_sdp(struct pjmedia_sdp_session *sdp) return 0; } -/*! \brief Helper function which determines if a transport is bound to any */ -static int multihomed_bound_any(pjsip_transport *transport) -{ - pj_uint32_t loop6[4] = {0, 0, 0, 0}; - - if ((transport->local_addr.addr.sa_family == pj_AF_INET() && - transport->local_addr.ipv4.sin_addr.s_addr == PJ_INADDR_ANY) || - (transport->local_addr.addr.sa_family == pj_AF_INET6() && - !pj_memcmp(&transport->local_addr.ipv6.sin6_addr, loop6, sizeof(loop6)))) { - return 1; - } - - return 0; -} - static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata) { + struct multihomed_message_restrictions *restrictions = ast_sip_mod_data_get(tdata->mod_data, multihomed_module.id, MOD_DATA_RESTRICTIONS); pjsip_tpmgr_fla2_param prm; pjsip_cseq_hdr *cseq; pjsip_via_hdr *via; + pjsip_fromto_hdr *from; /* Use the destination information to determine what local interface this message will go out on */ pjsip_tpmgr_fla2_param_default(&prm); @@ -153,6 +214,13 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata) ast_debug(4, "Re-wrote Contact URI host/port to %.*s:%d\n", (int)pj_strlen(&uri->host), pj_strbuf(&uri->host), uri->port); + if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP || + tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) { + uri->transport_param.slen = 0; + } else { + pj_strdup2(tdata->pool, &uri->transport_param, pjsip_transport_get_type_name(tdata->tp_info.transport->key.type)); + } + pjsip_tx_data_invalidate_msg(tdata); } } @@ -164,17 +232,38 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata) pjsip_tx_data_invalidate_msg(tdata); } + if (tdata->msg->type == PJSIP_REQUEST_MSG && (from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL)) && + (restrictions && !restrictions->disallow_from_domain_modification)) { + pjsip_name_addr *id_name_addr = (pjsip_name_addr *)from->uri; + pjsip_sip_uri *uri = pjsip_uri_get_uri(id_name_addr); + pj_sockaddr ip; + + if (pj_strcmp2(&uri->host, "localhost") && pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &uri->host, &ip) == PJ_SUCCESS) { + pj_strassign(&uri->host, &prm.ret_addr); + pjsip_tx_data_invalidate_msg(tdata); + } + } + /* Update the SDP if it is present */ if (tdata->msg->body && ast_sip_is_content_type(&tdata->msg->body->content_type, "application", "sdp") && multihomed_rewrite_sdp(tdata->msg->body->data)) { struct pjmedia_sdp_session *sdp = tdata->msg->body->data; + static const pj_str_t STR_IP4 = { "IP4", 3 }; + static const pj_str_t STR_IP6 = { "IP6", 3 }; + pj_str_t STR_IP; int stream; + STR_IP = tdata->tp_info.transport->key.type & PJSIP_TRANSPORT_IPV6 ? STR_IP6 : STR_IP4; + + pj_strassign(&sdp->origin.addr, &prm.ret_addr); + sdp->origin.addr_type = STR_IP; pj_strassign(&sdp->conn->addr, &prm.ret_addr); + sdp->conn->addr_type = STR_IP; for (stream = 0; stream < sdp->media_count; ++stream) { if (sdp->media[stream]->conn) { pj_strassign(&sdp->media[stream]->conn->addr, &prm.ret_addr); + sdp->media[stream]->conn->addr_type = STR_IP; } } @@ -184,42 +273,31 @@ static pj_status_t multihomed_on_tx_message(pjsip_tx_data *tdata) return PJ_SUCCESS; } -static pjsip_module multihomed_module = { - .name = { "Multihomed Routing", 18 }, - .id = -1, - .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 1, - .on_tx_request = multihomed_on_tx_message, - .on_tx_response = multihomed_on_tx_message, -}; - -static int unload_module(void) +void ast_res_pjsip_cleanup_message_ip_updater(void) { ast_sip_unregister_service(&multihomed_module); - return 0; + ast_sip_unregister_supplement(&multihomed_supplement); + ast_sip_session_unregister_supplement(&multihomed_session_supplement); } -static int load_module(void) +int ast_res_pjsip_init_message_ip_updater(void) { - char hostname[MAXHOSTNAMELEN] = ""; - - CHECK_PJSIP_MODULE_LOADED(); + if (ast_sip_session_register_supplement(&multihomed_session_supplement)) { + ast_log(LOG_ERROR, "Could not register multihomed session supplement for outgoing requests\n"); + return -1; + } - if (!gethostname(hostname, sizeof(hostname) - 1)) { - ast_verb(2, "Performing DNS resolution of local hostname '%s' to get local IPv4 and IPv6 address\n", - hostname); + if (ast_sip_register_supplement(&multihomed_supplement)) { + ast_log(LOG_ERROR, "Could not register multihomed supplement for outgoing requests\n"); + ast_res_pjsip_cleanup_message_ip_updater(); + return -1; } if (ast_sip_register_service(&multihomed_module)) { ast_log(LOG_ERROR, "Could not register multihomed module for incoming and outgoing requests\n"); - return AST_MODULE_LOAD_FAILURE; + ast_res_pjsip_cleanup_message_ip_updater(); + return -1; } - return AST_MODULE_LOAD_SUCCESS; + return 0; } - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Multihomed Routing Support", - .support_level = AST_MODULE_SUPPORT_CORE, - .load = load_module, - .unload = unload_module, - .load_pri = AST_MODPRI_APP_DEPEND, -); diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 68d5fdb563..a69aa1a743 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -59,11 +59,8 @@ ASTERISK_REGISTER_FILE() /*! \brief Scheduler for RTCP purposes */ static struct ast_sched_context *sched; -/*! \brief Address for IPv4 RTP */ -static struct ast_sockaddr address_ipv4; - -/*! \brief Address for IPv6 RTP */ -static struct ast_sockaddr address_ipv6; +/*! \brief Address for RTP */ +static struct ast_sockaddr address_rtp; static const char STR_AUDIO[] = "audio"; static const int FD_AUDIO = 0; @@ -173,11 +170,11 @@ static int rtp_check_timeout(const void *data) } /*! \brief Internal function which creates an RTP instance */ -static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, unsigned int ipv6) +static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media) { struct ast_rtp_engine_ice *ice; struct ast_sockaddr temp_media_address; - struct ast_sockaddr *media_address = ipv6 ? &address_ipv6 : &address_ipv4; + struct ast_sockaddr *media_address = &address_rtp; if (session->endpoint->media.bind_rtp_to_media_address && !ast_strlen_zero(session->endpoint->media.address)) { ast_sockaddr_parse(&temp_media_address, session->endpoint->media.address, 0); @@ -903,7 +900,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct } /* Using the connection information create an appropriate RTP instance */ - if (!session_media->rtp && create_rtp(session, session_media, ast_sockaddr_is_ipv6(addrs))) { + if (!session_media->rtp && create_rtp(session, session_media)) { return -1; } @@ -1055,7 +1052,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as pj_pool_t *pool = session->inv_session->pool_prov; static const pj_str_t STR_IN = { "IN", 2 }; static const pj_str_t STR_IP4 = { "IP4", 3}; - static const pj_str_t STR_IP6 = { "IP6", 3}; static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; pjmedia_sdp_media *media; @@ -1079,7 +1075,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as (!use_override_prefs && !ast_format_cap_has_type(session->endpoint->media.codecs, media_type))) { /* If no type formats are configured don't add a stream */ return 0; - } else if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->media.rtp.ipv6)) { + } else if (!session_media->rtp && create_rtp(session, session_media)) { return -1; } @@ -1120,7 +1116,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } media->conn->net_type = STR_IN; - media->conn->addr_type = session->endpoint->media.rtp.ipv6 ? STR_IP6 : STR_IP4; + /* Connection information will be updated by the multihomed module */ + media->conn->addr_type = STR_IP4; pj_strdup2(pool, &media->conn->addr, hostip); ast_rtp_instance_get_local_address(session_media->rtp, &addr); media->desc.port = direct_media_enabled ? ast_sockaddr_port(&session_media->direct_media_addr) : (pj_uint16_t) ast_sockaddr_port(&addr); @@ -1257,7 +1254,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a } /* Create an RTP instance if need be */ - if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->media.rtp.ipv6)) { + if (!session_media->rtp && create_rtp(session, session_media)) { return -1; } @@ -1493,8 +1490,7 @@ static int load_module(void) { CHECK_PJSIP_SESSION_MODULE_LOADED(); - ast_sockaddr_parse(&address_ipv4, "0.0.0.0", 0); - ast_sockaddr_parse(&address_ipv6, "::", 0); + ast_sockaddr_parse(&address_rtp, "::", 0); if (!(sched = ast_sched_context_create())) { ast_log(LOG_ERROR, "Unable to create scheduler context.\n"); diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c index 150336a085..b052cc428c 100644 --- a/res/res_pjsip_t38.c +++ b/res/res_pjsip_t38.c @@ -51,11 +51,8 @@ ASTERISK_REGISTER_FILE() /*! \brief The number of seconds after receiving a T.38 re-invite before automatically rejecting it */ #define T38_AUTOMATIC_REJECTION_SECONDS 5 -/*! \brief Address for IPv4 UDPTL */ -static struct ast_sockaddr address_ipv4; - -/*! \brief Address for IPv6 UDPTL */ -static struct ast_sockaddr address_ipv6; +/*! \brief Address for UDPTL */ +static struct ast_sockaddr address; /*! \brief T.38 state information */ struct t38_state { @@ -259,8 +256,7 @@ static int t38_initialize_session(struct ast_sip_session *session, struct ast_si return 0; } - if (!(session_media->udptl = ast_udptl_new_with_bindaddr(NULL, NULL, 0, - session->endpoint->media.t38.ipv6 ? &address_ipv6 : &address_ipv4))) { + if (!(session_media->udptl = ast_udptl_new_with_bindaddr(NULL, NULL, 0, &address))) { return -1; } @@ -922,8 +918,7 @@ static int load_module(void) { CHECK_PJSIP_SESSION_MODULE_LOADED(); - ast_sockaddr_parse(&address_ipv4, "0.0.0.0", 0); - ast_sockaddr_parse(&address_ipv6, "::", 0); + ast_sockaddr_parse(&address, "::", 0); if (ast_sip_session_register_supplement(&t38_supplement)) { ast_log(LOG_ERROR, "Unable to register T.38 session supplement\n"); diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 4bf625260f..cdf0c560d0 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -4971,6 +4971,15 @@ static int ast_rtp_fd(struct ast_rtp_instance *instance, int rtcp) static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct ast_sockaddr *addr) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + struct ast_sockaddr local, us; + + if (!ast_sockaddr_isnull(addr)) { + /* Update the local RTP address with what is being used */ + ast_ouraddrfor(addr, &us); + ast_rtp_instance_get_local_address(instance, &local); + ast_sockaddr_set_port(&us, ast_sockaddr_port(&local)); + ast_rtp_instance_set_local_address(instance, &us); + } if (rtp->rtcp) { ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance); @@ -4979,6 +4988,15 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1); } + + if (!ast_sockaddr_isnull(addr)) { + /* Update the local RTCP address with what is being used */ + ast_sockaddr_set_port(&us, ast_sockaddr_port(&local) + 1); + ast_sockaddr_copy(&rtp->rtcp->us, &us); + + ast_free(rtp->rtcp->local_addr_str); + rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&us)); + } } rtp->rxseqno = 0;