From: Justin T. Gibbs Date: Mon, 22 Dec 2025 01:30:10 +0000 (-0700) Subject: rtp/rtcp: Configure dual-stack behavior via IPV6_V6ONLY X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ce0eca443fbc6700ada7d90ee94504ecc0cd920e;p=thirdparty%2Fasterisk.git 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 + #include + +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 + #include + ], [ + 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);