From: epirat07@gmail.com To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Subject: Re: [FFmpeg-devel] [PATCH] avformat: enable UDP IPv6 multicast interface selection Date: Thu, 14 Mar 2024 10:52:38 +0100 Message-ID: <B7617203-3839-42D5-83E0-8BD43FCA8A8B@gmail.com> (raw) In-Reply-To: <AS8P193MB1997A4D7B829F68AAF85CA398D292@AS8P193MB1997.EURP193.PROD.OUTLOOK.COM> On 14 Mar 2024, at 10:22, Ignjatović, Lazar wrote: > avformat: enable UDP IPv6 multicast interface selection > > localaddr option now properly works with IPv6 addresses. Properly resolved > interface index in places where default 0 interface index is used (marked with > TODO: within udp.c). Added SO_BINDTODEVICE for mcast sockets that are used for > reading from the network. Need for this arises from the fact that [ffx1::*] and > [ffx2::*] mcast addresses need to have a defined interface for binding to avoid > ambiguity between multiple link-local networks on the same host. Failing to set > this option causes errors on Linux systems for interface and link-local scopes. > > 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/listening. > > Utilization of sin6_scope_id field enables usage and adequate resolving of > IPv6 addresses that utilize zone index (e.g. fe80::1ff:fe23:4567:890a%eth2 ) > This is not fully supported on Windows, thus relying on this field is not done > on Windows systems. > > Closes: #368 > > Signed-off-by: Lazar Ignjatovic <Lazar.Ignjatovic@cubic.com> > --- > configure | 3 ++ > libavformat/ip.c | 45 ++++++++++++++++++++++++ > libavformat/ip.h | 6 ++++ > libavformat/network.h | 6 ++++ > libavformat/udp.c | 80 ++++++++++++++++++++++++++++++++++++++----- > 5 files changed, 132 insertions(+), 8 deletions(-) > > diff --git a/configure b/configure > index c34bdd13f5..77f03948ce 100755 > --- a/configure > +++ b/configure > @@ -2256,6 +2256,7 @@ HEADERS_LIST=" > valgrind_valgrind_h > windows_h > winsock2_h > + iphlpapi_h > " > > INTRINSICS_LIST=" > @@ -6408,6 +6409,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 && network_extralibs+=" -liphlpapi" || disable iphlpapi_h > + check_func_headers iphlpapi.h GetBestInterfaceEx > else > disable network > fi > diff --git a/libavformat/ip.c b/libavformat/ip.c > index b2c7ef07e5..4f2d998c34 100644 > --- a/libavformat/ip.c > +++ b/libavformat/ip.c > @@ -18,6 +18,9 @@ > * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > */ > > +#define _DEFAULT_SOURCE > +#define _SVID_SOURCE > + > #include <string.h> > #include "ip.h" > #include "libavutil/avstring.h" > @@ -159,3 +162,45 @@ void ff_ip_reset_filters(IPSourceFilters *filters) > filters->nb_include_addrs = 0; > filters->nb_exclude_addrs = 0; > } > + > +unsigned int ff_ip_resolve_interface_index(struct sockaddr_storage *local_addr) > +{ > +#if HAVE_WINSOCK2_H && HAVE_IPHLPAPI_H > + DWORD retval; > + unsigned long iface; > + > + if (local_addr == NULL) > + return 0; > + > + retval = GetBestInterfaceEx((struct sockaddr*)local_addr, &iface); > + if (retval == NO_ERROR) > + return iface; > + > + return 0; > +#elif !HAVE_WINSOCK2_H > + struct ifaddrs *ifaddr, *ifa; > + > + if (local_addr == NULL) > + return 0; > + > + /* Special case for link-local addresses, relevant interface is stored in sin6_scope_id */ > +#if HAVE_STRUCT_SOCKADDR_IN6 && defined(IN6_IS_ADDR_LINKLOCAL) > + if (local_addr->ss_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6*)local_addr)->sin6_addr)) { > + unsigned int interface; > + interface = ((struct sockaddr_in6*)local_addr)->sin6_scope_id; > + > + if (interface != 0) > + return interface; > + } > +#endif > + if (getifaddrs(&ifaddr) == -1) > + return 0; > + > + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { > + if (ifa->ifa_addr != NULL && compare_addr((struct sockaddr_storage*)ifa->ifa_addr, local_addr) == 0) > + return if_nametoindex(ifa->ifa_name); > + } > + > + return 0; > +#endif > +} > diff --git a/libavformat/ip.h b/libavformat/ip.h > index b76cdab91c..4085e96f08 100644 > --- a/libavformat/ip.h > +++ b/libavformat/ip.h > @@ -69,4 +69,10 @@ int ff_ip_parse_blocks(void *log_ctx, const char *buf, IPSourceFilters *filters) > */ > void ff_ip_reset_filters(IPSourceFilters *filters); > > +/** > + * Resolves IP address to an associated interface index > + * @return interface index, 0 as default interface value on error > + */ > +unsigned int ff_ip_resolve_interface_index(struct sockaddr_storage *local_addr); > + > #endif /* AVFORMAT_IP_H */ > 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..9b8ca74282 100644 > --- a/libavformat/udp.c > +++ b/libavformat/udp.c > @@ -220,8 +220,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 = ff_ip_resolve_interface_index((struct sockaddr_storage *)local_addr); > 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 +230,38 @@ 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 = ff_ip_resolve_interface_index((struct sockaddr_storage *)local_addr); > + > + 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 +285,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 = ff_ip_resolve_interface_index((struct sockaddr_storage *)local_addr); > 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; > @@ -282,8 +312,7 @@ static int udp_set_multicast_sources(URLContext *h, > struct group_source_req mreqs; > int level = addr->sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; > > - //TODO: Interface index should be looked up from local_addr > - mreqs.gsr_interface = 0; > + mreqs.gsr_interface = ff_ip_resolve_interface_index(local_addr); > memcpy(&mreqs.gsr_group, addr, addr_len); > memcpy(&mreqs.gsr_source, &sources[i], sizeof(*sources)); > > @@ -839,10 +868,39 @@ static int udp_open(URLContext *h, const char *uri, int flags) > * port. This fails on windows. This makes sending to the same address > * using sendto() fail, so only do it if we're opened in read-only mode. */ > if (s->is_multicast && (h->flags & AVIO_FLAG_READ)) { > +#if defined(SO_BINDTODEVICE) && !HAVE_WINSOCK2_H > + { > + struct ifreq ifr; > + unsigned int iface; > + iface = ff_ip_resolve_interface_index((struct sockaddr_storage *)&s->local_addr_storage); > + memset(&ifr, 0, sizeof(ifr)); > + > + if (if_indextoname(iface, ifr.ifr_name)) { > + if (setsockopt(udp_fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { > + perror("setsockopt SO_BINDTODEVICE"); This should use the proper av_log logging instead. > + } > + } > + } > +#endif // defined(SO_BINDTODEVICE) && !HAVE_WINSOCK2_H > 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 +913,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 > > > This message has been marked as Public on 03/14/2024 09:22Z. > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > https://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > To unsubscribe, visit link above, or email > ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe". _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next prev parent reply other threads:[~2024-03-14 9:52 UTC|newest] Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top 2024-03-14 9:22 Ignjatović, Lazar (RS) 2024-03-14 9:52 ` epirat07 [this message] 2024-03-14 11:02 ` Ignjatović, Lazar (RS) 2024-03-15 16:26 ` Rémi Denis-Courmont 2024-03-19 9:40 ` Ignjatović, Lazar (RS) 2024-03-14 12:04 Ignjatović, Lazar (RS) 2024-03-14 21:13 ` Michael Niedermayer 2024-03-18 13:45 ` Ignjatović, Lazar (RS) 2024-03-19 1:23 ` Michael Niedermayer
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=B7617203-3839-42D5-83E0-8BD43FCA8A8B@gmail.com \ --to=epirat07@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git