Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
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".

      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