diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 80888c32ab..2d57208e65 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -737,6 +737,9 @@
; (default: "0")
;t38_udptl_nat=no ; Whether NAT support is enabled on UDPTL sessions
; (default: "no")
+;t38_bind_rtp_to_media_address= ; Bind the UDPTL session to the media_address.
+ ; This causes all UDPTL packets to be sent from
+ ; the specified address. (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/ast-db-manage/config/versions/a06d8f8462d9_add_t38_bind_udptl_to_media_address.py b/contrib/ast-db-manage/config/versions/a06d8f8462d9_add_t38_bind_udptl_to_media_address.py
new file mode 100644
index 0000000000..2d9a58cad8
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/a06d8f8462d9_add_t38_bind_udptl_to_media_address.py
@@ -0,0 +1,29 @@
+"""add t38_bind_udptl_to_media_address
+
+Revision ID: a06d8f8462d9
+Revises: f56d79a9f337
+Create Date: 2021-09-24 10:03:01.320480
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'a06d8f8462d9'
+down_revision = 'f56d79a9f337'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+AST_BOOL_NAME = 'ast_bool_values'
+AST_BOOL_VALUES = [ '0', '1',
+ 'off', 'on',
+ 'false', 'true',
+ 'no', 'yes' ]
+
+def upgrade():
+ ast_bool_values = ENUM(*AST_BOOL_VALUES, name=AST_BOOL_NAME, create_type=False)
+ op.add_column('ps_endpoints', sa.Column('t38_bind_udptl_to_media_address', ast_bool_values))
+
+
+def downgrade():
+ op.drop_column('ps_endpoints', 't38_bind_udptl_to_media_address')
diff --git a/doc/CHANGES-staging/res_pjsip_t38_bind_fixes.txt b/doc/CHANGES-staging/res_pjsip_t38_bind_fixes.txt
new file mode 100644
index 0000000000..d7bc8a1e9f
--- /dev/null
+++ b/doc/CHANGES-staging/res_pjsip_t38_bind_fixes.txt
@@ -0,0 +1,9 @@
+Subject: res_pjsip_t38
+
+In res_pjsip_sdp_rtp, the bind_rtp_to_media_address option and the
+fallback use of the transport's bind address solve problems sending
+media on systems that cannot send ipv4 packets on ipv6 sockets, and
+certain other situations. This change extends both of these behaviors
+to UDPTL sessions as well in res_pjsip_t38, to fix fax-specific
+problems on these systems, introducing a new option
+endpoint/t38_bind_udptl_to_media_address.
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 8c5b775bf5..7b9dcbd51d 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -760,6 +760,8 @@ struct ast_sip_t38_configuration {
unsigned int nat;
/*! Whether to use IPv6 for UDPTL or not */
unsigned int ipv6;
+ /*! Bind the UDPTL instance to the media_address */
+ unsigned int bind_udptl_to_media_address;
};
/*!
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index a375fe083b..2b30317738 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -940,6 +940,13 @@
When enabled the UDPTL stack will use IPv6.
+
+ Bind the UDPTL instance to the media_adress
+
+ If media_address is specified, this option causes the UDPTL instance to be bound to
+ the specified ip address which causes the packets to be sent from that address.
+
+
Set which country's indications to use for channels created for this endpoint.
@@ -2783,6 +2790,9 @@
+
+
+
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 6defa7cb96..c27b587076 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -2077,6 +2077,7 @@ int ast_res_pjsip_initialize_configuration(void)
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "fax_detect_timeout", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, faxdetect_timeout));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.nat));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.ipv6));
+ ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_bind_udptl_to_media_address", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.bind_udptl_to_media_address));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "tone_zone", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, zone));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "language", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, language));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "record_on_feature", "automixmon", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, info.recording.onfeature));
diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c
index 5d7f68d095..7df35d9b2a 100644
--- a/res/res_pjsip_t38.c
+++ b/res/res_pjsip_t38.c
@@ -255,11 +255,52 @@ static struct t38_state *t38_state_get_or_alloc(struct ast_sip_session *session)
/*! \brief Initializes UDPTL support on a session, only done when actually needed */
static int t38_initialize_session(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
{
+ struct ast_sockaddr temp_media_address;
+ struct ast_sockaddr *media_address = &address;
+
if (session_media->udptl) {
return 0;
}
- if (!(session_media->udptl = ast_udptl_new_with_bindaddr(NULL, NULL, 0, &address))) {
+ if (session->endpoint->media.t38.bind_udptl_to_media_address && !ast_strlen_zero(session->endpoint->media.address)) {
+ if (ast_sockaddr_parse(&temp_media_address, session->endpoint->media.address, 0)) {
+ ast_debug(5, "Endpoint %s: Binding UDPTL media to %s\n",
+ ast_sorcery_object_get_id(session->endpoint),
+ session->endpoint->media.address);
+ media_address = &temp_media_address;
+ } else {
+ ast_debug(5, "Endpoint %s: UDPTL media address invalid: %s\n",
+ ast_sorcery_object_get_id(session->endpoint),
+ session->endpoint->media.address);
+ }
+ } else {
+ struct ast_sip_transport *transport;
+
+ transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport",
+ session->endpoint->transport);
+ if (transport) {
+ struct ast_sip_transport_state *trans_state;
+
+ trans_state = ast_sip_get_transport_state(ast_sorcery_object_get_id(transport));
+ if (trans_state) {
+ char hoststr[PJ_INET6_ADDRSTRLEN];
+
+ pj_sockaddr_print(&trans_state->host, hoststr, sizeof(hoststr), 0);
+ if (ast_sockaddr_parse(&temp_media_address, hoststr, 0)) {
+ ast_debug(5, "Transport %s bound to %s: Using it for UDPTL media.\n",
+ session->endpoint->transport, hoststr);
+ media_address = &temp_media_address;
+ } else {
+ ast_debug(5, "Transport %s bound to %s: Invalid for UDPTL media.\n",
+ session->endpoint->transport, hoststr);
+ }
+ ao2_ref(trans_state, -1);
+ }
+ ao2_ref(transport, -1);
+ }
+ }
+
+ if (!(session_media->udptl = ast_udptl_new_with_bindaddr(NULL, NULL, 0, media_address))) {
return -1;
}