From: "Rémi Denis-Courmont" <remi@remlab.net> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Subject: Re: [FFmpeg-devel] [PATCH] avformat/udp: Select output interfaces for ipv6 multicast Date: Wed, 06 Aug 2025 22:28:59 +0700 Message-ID: <47824AC2-40F4-4FA4-B9A6-0F71646F4117@remlab.net> (raw) In-Reply-To: <20250805075618.1496509-1-peterend@axis.com> Le 5 août 2025 14:56:18 GMT+07:00, Peter Enderborg <peterend@axis.com> a écrit : >This fixes two old TODO's in ipv6 multicast handling. >A helper function to iterate over all interfaces added to >help join the multicast group on interface if approperite. > >The default value is IN6ADDR_ANY (::) and it then joins all interfaces >that are up and have multicast support. Local address can be >specified and then it join ALL interfaces that use that specific >local address. > >Limitations (TODO's) >Handling when network configuration is changed. if up/down etc. > >Signed-off-by: Peter Enderborg <peterend@axis.com> >--- > libavformat/udp.c | 108 ++++++++++++++++++++++++++++++++++++++-------- > 1 file changed, 90 insertions(+), 18 deletions(-) > >diff --git a/libavformat/udp.c b/libavformat/udp.c >index 035db785c2..0a981447a5 100644 >--- a/libavformat/udp.c >+++ b/libavformat/udp.c >@@ -68,6 +68,13 @@ > #include "libavutil/thread.h" > #endif > >+#if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6) >+#include <sys/types.h> >+#include <net/if.h> >+#include <sys/ioctl.h> >+#include <ifaddrs.h> >+#endif >+ > #ifndef IPV6_ADD_MEMBERSHIP > #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP > #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP >@@ -205,6 +212,79 @@ static int udp_set_multicast_ttl(int sockfd, int mcastTTL, > > return 0; > } >+#if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6) >+static int udp_ipv6_multicast_iterate(int sockfd, struct sockaddr_in6 *addr, >+ struct sockaddr_in6 *local_addr, int mop, void *logctx) >+{ >+ struct in6_addr anyipv6 = IN6ADDR_ANY_INIT; >+ struct ifaddrs *ifal=NULL,*ife=NULL; >+ int iindex_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); >+ int membership_changed = 0; >+ >+ if (iindex_fd >= 0 && !getifaddrs(&ifal)) { >+ for(ife=ifal; ife!=NULL; ife=ife->ifa_next) { >+ if (ife->ifa_addr && >+ (ife->ifa_addr->sa_family == AF_INET6) && >+ (ife->ifa_flags & IFF_MULTICAST) && >+ (ife->ifa_flags & IFF_UP)) { >+ if ((!memcmp(&local_addr->sin6_addr, &anyipv6, sizeof(struct in6_addr))) || >+ (!memcmp(&local_addr->sin6_addr, &((struct sockaddr_in6 *)ife->ifa_addr)->sin6_addr, sizeof(struct in6_addr)))) { >+ struct ifreq if_req; >+ strncpy(if_req.ifr_name, ife->ifa_name, IFNAMSIZ); >+ if (ioctl(iindex_fd, SIOCGIFINDEX, &if_req) != -1) { >+ struct ipv6_mreq mreq6; >+ memcpy(&mreq6.ipv6mr_multiaddr, &addr->sin6_addr, sizeof(struct in6_addr)); >+ mreq6.ipv6mr_interface = if_req.ifr_ifindex; >+ switch (mop) { >+ case IPV6_JOIN_GROUP: >+ if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) == 0) { >+ membership_changed = 1; >+ } else { >+ /* Subscribe for all ipv6 addresses belong to a >+ interface.It can generate EADDRINUSE but >+ is accaptable. */ >+ if (errno != EADDRINUSE) { >+ ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IPV6_ADD_MEMBERSHIP)"); >+ goto error; >+ } >+ } >+ break; >+ case IPV6_LEAVE_GROUP: >+ if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) == 0) { >+ membership_changed = 1; You can't leave a group you didn't join and you can't assume that the set of interfaces is constant over time. Besides joining a group on more than one interface is generally a terrible idea, as you would receive the same data multiple time (+ reordering). It makes sense to track interfaces and subscribe to all of them for discovery protocols, but it's way more involved (and less portable) than this. >+ } else { >+ /* Unsubscribe for all ipv6 addresses belong to a >+ interface. Ignore EADDRNOTAVAIL. */ >+ if (errno != EADDRNOTAVAIL) { >+ ff_log_net_error(logctx, AV_LOG_ERROR, "setsockopt(IPV6_DROP_MEMBERSHIP)"); >+ goto error; >+ } >+ } >+ break; >+ default: >+ av_log(logctx, AV_LOG_ERROR, "unknown multicast operation\n"); >+ goto error; >+ } >+ } >+ } >+ } >+ } >+ } >+ freeifaddrs(ifal); >+ close(iindex_fd); >+ >+ if (!membership_changed) { >+ av_log(logctx, AV_LOG_ERROR, "no valid interfaces found\n"); >+ return AVERROR(EINVAL); >+ } >+ return 0; >+error: >+ freeifaddrs(ifal); >+ close(iindex_fd); >+ av_log(logctx, AV_LOG_ERROR, "udp_ipv6_multicast_iterate failed\n"); >+ return AVERROR(EINVAL); >+} >+#endif > > static int udp_join_multicast_group(int sockfd, struct sockaddr *addr, > struct sockaddr *local_addr, void *logctx) >@@ -226,15 +306,11 @@ static int udp_join_multicast_group(int sockfd, struct sockaddr *addr, > #endif > #if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6) > if (addr->sa_family == AF_INET6) { >- 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; >- 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(); >- } >+ return udp_ipv6_multicast_iterate(sockfd, >+ (struct sockaddr_in6 *)addr, >+ (struct sockaddr_in6 *)local_addr, >+ IPV6_JOIN_GROUP, >+ logctx); > } > #endif > return 0; >@@ -260,15 +336,11 @@ static int udp_leave_multicast_group(int sockfd, struct sockaddr *addr, > #endif > #if HAVE_STRUCT_IPV6_MREQ && defined(IPPROTO_IPV6) > if (addr->sa_family == AF_INET6) { >- 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; >- 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; >- } >+ return udp_ipv6_multicast_iterate(sockfd, >+ (struct sockaddr_in6 *)addr, >+ (struct sockaddr_in6 *)local_addr, >+ IPV6_LEAVE_GROUP, >+ logctx); > } > #endif > return 0; _______________________________________________ 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".
prev parent reply other threads:[~2025-08-06 15:29 UTC|newest] Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top 2025-08-05 7:56 Peter Enderborg 2025-08-06 15:28 ` Rémi Denis-Courmont [this message]
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=47824AC2-40F4-4FA4-B9A6-0F71646F4117@remlab.net \ --to=remi@remlab.net \ --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