Commit ce0eca443f for asterisk.org
commit ce0eca443fbc6700ada7d90ee94504ecc0cd920e
Author: Justin T. Gibbs <gibbs@scsiguy.com>
Date: Sun Dec 21 18:30:10 2025 -0700
rtp/rtcp: Configure dual-stack behavior via IPV6_V6ONLY
Dual-stack behavior (simultaneous listening for IPV4 and IPV6
connections on a single socket) is required by Asterisk's ICE
implementation. On systems with the IPV6_V6ONLY sockopt, set
the option to 0 (dual-stack enabled) when binding to the IPV6
any address. This ensures correct behavior regardless of the
system's default dual-stack configuration.
diff --git a/configure b/configure
index d35e13c1d5..f32eecd79b 100755
--- a/configure
+++ b/configure
@@ -31433,6 +31433,40 @@ esac
fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if setsockopt() accepts the IPV6_V6ONLY socket option" >&5
+printf %s "checking if setsockopt() accepts the IPV6_V6ONLY socket option... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+
+int
+main (void)
+{
+
+ int opt = IPV6_V6ONLY;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+printf "%s\n" "#define HAVE_SOCK_IPV6_V6ONLY 1" >>confdefs.h
+
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+
+
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can increase the maximum select-able file descriptor" >&5
printf %s "checking if we can increase the maximum select-able file descriptor... " >&6; }
if test "$cross_compiling" = yes
diff --git a/configure.ac b/configure.ac
index 2a16142b98..b0dd1946c4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1248,6 +1248,19 @@ AC_RUN_IFELSE(
AC_MSG_RESULT(cross-compile)
)
+AC_MSG_CHECKING([if setsockopt() accepts the IPV6_V6ONLY socket option])
+AC_COMPILE_IFELSE(
+[AC_LANG_PROGRAM([
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ ], [
+ int opt = IPV6_V6ONLY;
+ ])],
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_SOCK_IPV6_V6ONLY, 1, [Define to 1 if your socket() implementation has the IPV6_V6ONLY socket option.]),
+ AC_MSG_RESULT(no)
+)
+
AC_MSG_CHECKING(if we can increase the maximum select-able file descriptor)
AC_RUN_IFELSE(
[AC_LANG_PROGRAM([
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 1df046852d..a3c1fc719f 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -914,6 +914,9 @@
/* Define to 1 if your socket() implementation can accept SOCK_NONBLOCK. */
#undef HAVE_SOCK_NONBLOCK
+/* Define to 1 if your socket() implementation has the IPV6_V6ONLY socket option. */
+#undef HAVE_SOCK_IPV6_V6ONLY
+
/* Define to 1 if your system has soxmix application. */
#undef HAVE_SOXMIX
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index f258b41fd9..2a8489d4f1 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -3565,9 +3565,13 @@ static void calc_mean_and_standard_deviation(double new_sample, double *mean, do
*std_dev = sqrt((last_sum_of_squares + (delta1 * delta2)) / *count);
}
-static int create_new_socket(const char *type, int af)
+static int create_new_socket(const char *type, struct ast_sockaddr *bind_addr)
{
- int sock = ast_socket_nonblock(af, SOCK_DGRAM, 0);
+ int af, sock;
+
+ af = ast_sockaddr_is_ipv4(bind_addr) ? AF_INET :
+ ast_sockaddr_is_ipv6(bind_addr) ? AF_INET6 : -1;
+ sock = ast_socket_nonblock(af, SOCK_DGRAM, 0);
if (sock < 0) {
ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno));
@@ -3580,6 +3584,15 @@ static int create_new_socket(const char *type, int af)
}
#endif
+#ifdef HAVE_SOCK_IPV6_V6ONLY
+ if (AF_INET6 == af && ast_sockaddr_is_any(bind_addr)) {
+ /* ICE relies on dual-stack behavior. Ensure it is enabled. */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof(int)) != 0) {
+ ast_log(LOG_WARNING, "setsockopt IPV6_V6ONLY=0 failed: %s\n", strerror(errno));
+ }
+ }
+#endif
+
return sock;
}
@@ -4041,10 +4054,7 @@ static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_
rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_CLOSED : STRICT_RTP_OPEN);
/* Create a new socket for us to listen on and use */
- if ((rtp->s =
- create_new_socket("RTP",
- ast_sockaddr_is_ipv4(&rtp->bind_address) ? AF_INET :
- ast_sockaddr_is_ipv6(&rtp->bind_address) ? AF_INET6 : -1)) < 0) {
+ if ((rtp->s = create_new_socket("RTP", &rtp->bind_address)) < 0) {
ast_log(LOG_WARNING, "Failed to create a new socket for RTP instance '%p'\n", instance);
return -1;
}
@@ -8938,12 +8948,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
* switching from MUX. Either way, we won't have
* a socket set up, and we need to set it up
*/
- if ((rtp->rtcp->s =
- create_new_socket("RTCP",
- ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
- AF_INET :
- ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
- AF_INET6 : -1)) < 0) {
+ if ((rtp->rtcp->s = create_new_socket("RTCP", &rtp->rtcp->us)) < 0) {
ast_debug_rtcp(1, "(%p) RTCP failed to create a new socket\n", instance);
ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);