[FFmpeg-devel] [PATCH v2] avformat: enable UDP IPv6 multicast interface selection using zone index

Lynne dev at lynne.ee
Thu Apr 11 10:50:01 EEST 2024


Apr 11, 2024, 09:45 by Lazar.Ignjatovic at cubic.com:

> avformat: enable UDP IPv6 multicast interface selection using zone index
>
> Enabled IPv6 interface selection using zone index. Properly resolved
> interface index in places where default 0 interface index is used
> (marked with TODO: within udp.c). Adjusted binding for multicast sockets
> that are used for reading from the network.
>
> For mcast addresses, bind to mcast address is attempted as before.
> In case that this fails, which will happen on Windows, socket is bound
> to INADDR_ANY/IN6ADDR_ANY_INIT depending on address family. Actual
> interface selection is performed using udp_set_multicast_interface to
> point to the desired interface for sending.
>
> Closes: #368
>
> Signed-off-by: Lazar Ignjatovic <Lazar.Ignjatovic at cubic.com>
> ---
> V1 -> V2 reverted iface resolution for IPv4 MCAST_JOIN_SOURCE_GROUP
> NOTE: Due to comments, this patch is proposed as one of two alternatives
> The other alternative uses `localaddr` for defining interfaces.
>
>
>  configure             |  3 ++
>  doc/protocols.texi    |  2 +-
>  libavformat/network.h |  6 +++
>  libavformat/udp.c     | 85 +++++++++++++++++++++++++++++++++++++++----
>  4 files changed, 88 insertions(+), 8 deletions(-)
>
> diff --git a/configure b/configure
> index 2a1d22310b..35d6a0b78c 100755
> --- a/configure
> +++ b/configure
> @@ -2307,6 +2307,7 @@ HEADERS_LIST="
>  valgrind_valgrind_h
>  windows_h
>  winsock2_h
> +    iphlpapi_h
>  "
>
>  INTRINSICS_LIST="
> @@ -6475,6 +6476,8 @@ if ! disabled network; then
>  check_struct winsock2.h "struct sockaddr" sa_len
>  check_type ws2tcpip.h "struct sockaddr_in6"
>  check_type ws2tcpip.h "struct sockaddr_storage"
> +        check_headers iphlpapi.h -liphlpapi && network_extralibs="$network_extralibs -liphlpapi" || disable iphlpapi_h
> +        check_func_headers iphlpapi.h GetBestInterfaceEx $network_extralibs
>  else
>  disable network
>  fi
> diff --git a/doc/protocols.texi b/doc/protocols.texi
> index f54600b846..a8892845d3 100644
> --- a/doc/protocols.texi
> +++ b/doc/protocols.texi
> @@ -2027,7 +2027,7 @@ packet bursts.
>  Override the local UDP port to bind with.
>
>  @item localaddr=@var{addr}
> -Local IP address of a network interface used for sending packets or joining
> +Local IPv4 address of a network interface used for sending packets or joining
>  multicast groups.
>
>  @item pkt_size=@var{size}
> diff --git a/libavformat/network.h b/libavformat/network.h
> index ca214087fc..2461b651d4 100644
> --- a/libavformat/network.h
> +++ b/libavformat/network.h
> @@ -38,6 +38,10 @@
>  #include <winsock2.h>
>  #include <ws2tcpip.h>
>
> +#if HAVE_IPHLPAPI_H
> +#include <iphlpapi.h>
> +#endif
> +
>  #ifndef EPROTONOSUPPORT
>  #define EPROTONOSUPPORT WSAEPROTONOSUPPORT
>  #endif
> @@ -64,6 +68,8 @@ int ff_neterrno(void);
>  #include <netinet/in.h>
>  #include <netinet/tcp.h>
>  #include <netdb.h>
> +#include <net/if.h>
> +#include <ifaddrs.h>
>
>  #define ff_neterrno() AVERROR(errno)
>  #endif /* HAVE_WINSOCK2_H */
> diff --git a/libavformat/udp.c b/libavformat/udp.c
> index d9514f5026..00c73f9ec9 100644
> --- a/libavformat/udp.c
> +++ b/libavformat/udp.c
> @@ -35,6 +35,7 @@
>  #include "libavutil/opt.h"
>  #include "libavutil/log.h"
>  #include "libavutil/time.h"
> +#include "libavutil/avstring.h"
>  #include "internal.h"
>  #include "network.h"
>  #include "os_support.h"
> @@ -220,8 +221,7 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr,
>  struct ipv6_mreq mreq6;
>
>  memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr));
> -        //TODO: Interface index should be looked up from local_addr
> -        mreq6.ipv6mr_interface = 0;
> +        mreq6.ipv6mr_interface = ((struct sockaddr_in6 *)addr)->sin6_scope_id;
>  if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
>  ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IPV6_ADD_MEMBERSHIP)");
>  return ff_neterrno();
> @@ -231,6 +231,39 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr,
>  return 0;
>  }
>
> +static int udp_set_multicast_interface(int sockfd, struct sockaddr *addr,
> +                                    struct sockaddr *local_addr, void *logctx)
> +{
> +#ifdef IP_MULTICAST_IF
> +    if (addr->sa_family == AF_INET) {
> +        struct ip_mreq mreq;
> +
> +        mreq.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
> +        if (local_addr)
> +            mreq.imr_interface = ((struct sockaddr_in *)local_addr)->sin_addr;
> +        else
> +            mreq.imr_interface.s_addr = INADDR_ANY;
> +
> +        if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_IF, (const void *)&mreq, sizeof(mreq)) < 0) {
> +            ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IP_MULTICAST_IF)");
> +            return ff_neterrno();
> +        }
> +    }
> +#endif
> +#if defined(IPV6_MULTICAST_IF) && defined(IPPROTO_IPV6)
> +    if (addr->sa_family == AF_INET6) {
> +        unsigned int iface;
> +        iface = ((struct sockaddr_in6 *)addr)->sin6_scope_id;
> +
> +        if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(unsigned int)) < 0) {
> +            ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IPV6_MULTICAST_IF)");
> +            return ff_neterrno();
> +        }
> +    }
> +#endif
> +    return 0;
> +}
> +
>  static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr,
>  struct sockaddr *local_addr, void *logctx)
>  {
> @@ -254,8 +287,7 @@ static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr,
>  struct ipv6_mreq mreq6;
>
>  memcpy(&mreq6.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)addr)->sin6_addr), sizeof(struct in6_addr));
> -        //TODO: Interface index should be looked up from local_addr
> -        mreq6.ipv6mr_interface = 0;
> +        mreq6.ipv6mr_interface = ((struct sockaddr_in6 *)addr)->sin6_scope_id;
>  if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
>  ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IPV6_DROP_MEMBERSHIP)");
>  return -1;
> @@ -284,6 +316,9 @@ static int udp_set_multicast_sources(URLContext *h,
>
>  //TODO: Interface index should be looked up from local_addr
>  mreqs.gsr_interface = 0;
> +            if (level == IPPROTO_IPV6)
> +                mreqs.gsr_interface = ((struct sockaddr_in6 *)addr)->sin6_scope_id;
> +
>  memcpy(&mreqs.gsr_group, addr, addr_len);
>  memcpy(&mreqs.gsr_source, &sources[i], sizeof(*sources));
>
> @@ -340,9 +375,24 @@ static int udp_set_url(URLContext *h,
>  {
>  struct addrinfo *res0;
>  int addr_len;
> +    const char *host, *sc;
> +    char address[1024], scope[1024];
>
> -    res0 = ff_ip_resolve_host(h, hostname, port, SOCK_DGRAM, AF_UNSPEC, 0);
> +    if (sc = strchr(hostname, '%')) {
> +        av_strlcpy(address, hostname, FFMIN(1024, sc - hostname + 1));
> +        av_strlcpy(scope, sc + 1, FFMIN(1024, strlen(hostname) - (sc - hostname)));
> +        host = address;
> +    } else {
> +        host = hostname;
> +    }
> +
> +    res0 = ff_ip_resolve_host(h, host, port, SOCK_DGRAM, AF_UNSPEC, 0);
>  if (!res0) return AVERROR(EIO);
> +#if HAVE_IPHLPAPI_H || !HAVE_WINSOCK2_H
> +    if (res0->ai_family== AF_INET6 && strlen(scope) > 0) {
> +        ((struct sockaddr_in6*)res0->ai_addr)->sin6_scope_id = if_nametoindex(scope);
> +    }
> +#endif
>  memcpy(addr, res0->ai_addr, res0->ai_addrlen);
>  addr_len = res0->ai_addrlen;
>  freeaddrinfo(res0);
> @@ -841,8 +891,23 @@ static int udp_open(URLContext *h, const char *uri, int flags)
>  if (s->is_multicast && (h->flags & AVIO_FLAG_READ)) {
>  bind_ret = bind(udp_fd,(struct sockaddr *)&s->dest_addr, len);
>  }
> -    /* bind to the local address if not multicast or if the multicast
> -     * bind failed */
> +
> +    /* bind to ADDR_ANY if the multicast bind failed */
> +    if (s->is_multicast && bind_ret < 0) {
> +        struct addrinfo *res;
> +
> +        if (s->dest_addr.ss_family == AF_INET)
> +            res = ff_ip_resolve_host(h, "0.0.0.0", 0, SOCK_DGRAM, AF_UNSPEC, 0);
> +        else if (s->dest_addr.ss_family == AF_INET6)
> +            res = ff_ip_resolve_host(h, "::", 0, SOCK_DGRAM, AF_UNSPEC, 0);
> +
> +        if (res && res->ai_addr) {
> +            bind_ret = bind(udp_fd, res->ai_addr, res->ai_addrlen);
> +        }
> +
> +        freeaddrinfo(res);
> +    }
> +
>  /* the bind is needed to give a port to the socket now */
>  if (bind_ret < 0 && bind(udp_fd,(struct sockaddr *)&my_addr, len) < 0) {
>  ff_log_net_error(h, AV_LOG_ERROR, "bind failed");
> @@ -855,6 +920,12 @@ static int udp_open(URLContext *h, const char *uri, int flags)
>  s->local_port = udp_port(&my_addr, len);
>
>  if (s->is_multicast) {
> +        if ((ret = udp_set_multicast_interface(udp_fd,
> +                                        (struct sockaddr *)&s->dest_addr,
> +                                        (struct sockaddr *)&s->local_addr_storage,
> +                                        h)) < 0)
> +            goto fail;
> +
>  if (h->flags & AVIO_FLAG_WRITE) {
>  /* output */
>  if ((ret = udp_set_multicast_ttl(udp_fd, s->ttl, (struct sockaddr *)&s->dest_addr, h)) < 0)
> --
> 2.41.0.windows.2
>

Is there a reason why we can't switch to IPv4 addresses mapped
in IPv6 and just use the IPv6 API everywhere?


More information about the ffmpeg-devel mailing list