Commit da773a8e authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

Rework multicast join

 * Do not force definitions on Windows so that compilation works with
   all variants of MingW (this may break IPv6 SSM with old compilers).
 * Do not fall back to legacy IPv4-specific ioctls if RFC3678 is
   supported. This avoids compiling dead code and works around issues
   with Linux kernel headers (especially Android).
 * Do not attempt to set multicast interface with legacy IPv4 ioctls.
   It is not very clear whether this is the address or the index.
   Also, "--miface-addr" was very confusing as it was mostly ignored in
   favor of "--miface".
parent 6e18f208
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <vlc_common.h> #include <vlc_common.h>
#include <errno.h> #include <errno.h>
#include <assert.h>
#include <vlc_network.h> #include <vlc_network.h>
...@@ -321,107 +322,20 @@ static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family, ...@@ -321,107 +322,20 @@ static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family,
} }
/** static unsigned var_GetIfIndex (vlc_object_t *obj)
* Old-style any-source multicast join.
* In use on Windows XP/2003 and older.
*/
static int
net_IPv4Join (vlc_object_t *obj, int fd,
const struct sockaddr_in *src, const struct sockaddr_in *grp)
{
#ifdef IP_ADD_MEMBERSHIP
union
{
struct ip_mreq gr4;
# ifdef IP_ADD_SOURCE_MEMBERSHIP
struct ip_mreq_source gsr4;
# endif
} opt;
int cmd;
struct in_addr id = { .s_addr = INADDR_ANY };
socklen_t optlen;
/* Multicast interface IPv4 address */
char *iface = var_InheritString (obj, "miface-addr");
if ((iface != NULL)
&& (inet_pton (AF_INET, iface, &id) <= 0))
{
msg_Err (obj, "invalid multicast interface address %s", iface);
free (iface);
goto error;
}
free (iface);
memset (&opt, 0, sizeof (opt));
if (src != NULL)
{
# if defined( IP_ADD_SOURCE_MEMBERSHIP ) && !defined( __ANDROID__ )
cmd = IP_ADD_SOURCE_MEMBERSHIP;
opt.gsr4.imr_multiaddr = grp->sin_addr;
opt.gsr4.imr_sourceaddr = src->sin_addr;
opt.gsr4.imr_interface = id;
optlen = sizeof (opt.gsr4);
# else
errno = ENOSYS;
goto error;
# endif
}
else
{
cmd = IP_ADD_MEMBERSHIP;
opt.gr4.imr_multiaddr = grp->sin_addr;
opt.gr4.imr_interface = id;
optlen = sizeof (opt.gr4);
}
msg_Dbg (obj, "IP_ADD_%sMEMBERSHIP multicast request",
(src != NULL) ? "SOURCE_" : "");
if (setsockopt (fd, SOL_IP, cmd, &opt, optlen) == 0)
return 0;
error:
#endif
msg_Err (obj, "cannot join IPv4 multicast group (%m)");
return -1;
}
static int
net_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src)
{ {
#ifdef IPV6_JOIN_GROUP char *ifname = var_InheritString (obj, "miface");
struct ipv6_mreq gr6; if (ifname == NULL)
memset (&gr6, 0, sizeof (gr6));
gr6.ipv6mr_interface = src->sin6_scope_id;
memcpy (&gr6.ipv6mr_multiaddr, &src->sin6_addr, 16);
msg_Dbg (obj, "IPV6_JOIN_GROUP multicast request");
if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP, &gr6, sizeof (gr6)))
return 0; return 0;
#else
errno = ENOSYS;
#endif
msg_Err (obj, "cannot join IPv6 any-source multicast group (%m)"); unsigned ifindex = if_nametoindex (ifname);
return -1; if (ifindex == 0)
msg_Err (obj, "invalid multicast interface: %s", ifname);
free (ifname);
return ifindex;
} }
#if defined (WIN32) && !defined (MCAST_JOIN_SOURCE_GROUP)
/*
* I hate manual definitions: Error-prone. Portability hell.
* Developers shall use UP-TO-DATE compilers. Full point.
* If you remove the warning, you remove the whole ifndef.
*/
# warning Your C headers are out-of-date. Please update.
# define MCAST_JOIN_GROUP 41
# define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
#endif
/** /**
* IP-agnostic multicast join, * IP-agnostic multicast join,
* with fallback to old APIs, and fallback from SSM to ASM. * with fallback to old APIs, and fallback from SSM to ASM.
...@@ -431,151 +345,162 @@ net_SourceSubscribe (vlc_object_t *obj, int fd, ...@@ -431,151 +345,162 @@ net_SourceSubscribe (vlc_object_t *obj, int fd,
const struct sockaddr *src, socklen_t srclen, const struct sockaddr *src, socklen_t srclen,
const struct sockaddr *grp, socklen_t grplen) const struct sockaddr *grp, socklen_t grplen)
{ {
int level, iid = 0; #ifdef MCAST_JOIN_SOURCE_GROUP
/* Agnostic SSM multicast join */
int level;
struct group_source_req gsr;
char *iface = var_InheritString (obj, "miface"); memset (&gsr, 0, sizeof (gsr));
if (iface != NULL) gsr.gsr_interface = var_GetIfIndex (obj);
{
iid = if_nametoindex (iface);
if (iid == 0)
{
msg_Err (obj, "invalid multicast interface: %s", iface);
free (iface);
return -1;
}
free (iface);
}
switch (grp->sa_family) switch (grp->sa_family)
{ {
#ifdef AF_INET6 #ifdef AF_INET6
case AF_INET6: case AF_INET6:
{
const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
level = SOL_IPV6; level = SOL_IPV6;
if (((const struct sockaddr_in6 *)grp)->sin6_scope_id) assert (grplen >= sizeof (struct sockaddr_in6));
iid = ((const struct sockaddr_in6 *)grp)->sin6_scope_id; if (g6->sin6_scope_id != 0)
gsr.gsr_interface = g6->sin6_scope_id;
break; break;
}
#endif #endif
case AF_INET: case AF_INET:
level = SOL_IP; level = SOL_IP;
break; break;
default: default:
errno = EAFNOSUPPORT; errno = EAFNOSUPPORT;
return -1; return -1;
} }
if (src != NULL) assert (grplen <= sizeof (gsr.gsr_group));
switch (src->sa_family) memcpy (&gsr.gsr_source, src, srclen);
{ assert (srclen <= sizeof (gsr.gsr_source));
#ifdef AF_INET6 memcpy (&gsr.gsr_group, grp, grplen);
case AF_INET6: if (setsockopt (fd, level, MCAST_JOIN_SOURCE_GROUP,
if (memcmp (&((const struct sockaddr_in6 *)src)->sin6_addr, &gsr, sizeof (gsr)) == 0)
&in6addr_any, sizeof (in6addr_any)) == 0) return 0;
src = NULL;
break;
#endif
case AF_INET:
if (((const struct sockaddr_in *)src)->sin_addr.s_addr
== INADDR_ANY)
src = NULL;
break;
}
/* Agnostic ASM/SSM multicast join */ #else
#ifdef MCAST_JOIN_SOURCE_GROUP if (src->sa_family != grp->sa_family)
union
{ {
struct group_req gr; errno = EAFNOSUPPORT;
struct group_source_req gsr; return -1;
} opt; }
socklen_t optlen;
memset (&opt, 0, sizeof (opt));
if (src != NULL) switch (grp->sa_family)
{ {
if ((grplen > sizeof (opt.gsr.gsr_group)) # ifdef IP_ADD_SOURCE_MEMBERSHIP
|| (srclen > sizeof (opt.gsr.gsr_source))) /* IPv4-specific API */
return -1; case AF_INET:
{
opt.gsr.gsr_interface = iid; struct ip_mreq_source imr;
memcpy (&opt.gsr.gsr_source, src, srclen);
memcpy (&opt.gsr.gsr_group, grp, grplen); memset (&imr, 0, sizeof (imr));
optlen = sizeof (opt.gsr); assert (grplen >= sizeof (struct sockaddr_in));
imr.imr_multiaddr = ((const struct sockaddr_in *)grp)->sin_addr;
assert (srclen >= sizeof (struct sockaddr_in));
imr.imr_sourceaddr = ((const struct sockaddr_in *)src)->sin_addr;
if (setsockopt (fd, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP,
&imr, sizeof (imr)) == 0)
return 0;
break;
}
# endif
default:
errno = EAFNOSUPPORT;
} }
else
{
if (grplen > sizeof (opt.gr.gr_group))
return -1;
opt.gr.gr_interface = iid; #endif
memcpy (&opt.gr.gr_group, grp, grplen); msg_Err (obj, "cannot join source multicast group: %m");
optlen = sizeof (opt.gr); msg_Warn (obj, "trying ASM instead of SSM...");
} return net_Subscribe (obj, fd, grp, grplen);
}
msg_Dbg (obj, "Multicast %sgroup join request", src ? "source " : "");
if (setsockopt (fd, level, int net_Subscribe (vlc_object_t *obj, int fd,
src ? MCAST_JOIN_SOURCE_GROUP : MCAST_JOIN_GROUP, const struct sockaddr *grp, socklen_t grplen)
(void *)&opt, optlen) == 0) {
return 0; #ifdef MCAST_JOIN_GROUP
#endif /* Agnostic SSM multicast join */
int level;
struct group_req gr;
/* Fallback to IPv-specific APIs */ memset (&gr, 0, sizeof (gr));
if ((src != NULL) && (src->sa_family != grp->sa_family)) gr.gr_interface = var_GetIfIndex (obj);
return -1;
switch (grp->sa_family) switch (grp->sa_family)
{ {
case AF_INET:
if ((grplen < sizeof (struct sockaddr_in))
|| ((src != NULL) && (srclen < sizeof (struct sockaddr_in))))
return -1;
if (net_IPv4Join (obj, fd, (const struct sockaddr_in *)src,
(const struct sockaddr_in *)grp) == 0)
return 0;
break;
#ifdef AF_INET6 #ifdef AF_INET6
case AF_INET6: case AF_INET6:
if ((grplen < sizeof (struct sockaddr_in6)) {
|| ((src != NULL) && (srclen < sizeof (struct sockaddr_in6)))) const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
return -1;
/* IPv6-specific SSM API does not exist. So if we're here
* it means IPv6 SSM is not supported on this OS and we
* directly fallback to ASM */
if (net_IPv6Join (obj, fd, (const struct sockaddr_in6 *)grp) == 0) level = SOL_IPV6;
return 0; assert (grplen >= sizeof (struct sockaddr_in6));
if (g6->sin6_scope_id != 0)
gr.gr_interface = g6->sin6_scope_id;
break; break;
}
#endif #endif
case AF_INET:
level = SOL_IP;
break;
default:
errno = EAFNOSUPPORT;
return -1;
} }
msg_Err (obj, "Multicast group join error (%m)"); assert (grplen <= sizeof (gr.gr_group));
memcpy (&gr.gr_group, grp, grplen);
if (setsockopt (fd, level, MCAST_JOIN_GROUP, &gr, sizeof (gr)) == 0)
return 0;
if (src != NULL) #else
switch (grp->sa_family)
{ {
msg_Warn (obj, "Trying ASM instead of SSM..."); # ifdef IPV6_JOIN_GROUP
return net_Subscribe (obj, fd, grp, grplen); case AF_INET6:
{
struct ipv6_mreq ipv6mr;
const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
memset (&ipv6mr, 0, sizeof (ipv6mr));
assert (grplen >= sizeof (struct sockaddr_in6));
ipv6mr.ipv6mr_multiaddr = g6->sin6_addr;
ipv6mr.ipv6mr_interface = g6->sin6_scope_id;
if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP,
&ipv6mr, sizeof (ipv6mr)))
return 0;
break;
}
# endif
# ifdef IP_ADD_MEMBERSHIP
case AF_INET:
{
struct ip_mreq imr;
memset (&imr, 0, sizeof (imr));
assert (grplen >= sizeof (struct sockaddr_in));
imr.imr_multiaddr = ((const struct sockaddr_in *)grp)->sin_addr;
if (setsockopt (fd, SOL_IP, IP_ADD_MEMBERSHIP,
&imr, sizeof (imr)) == 0)
return 0;
break;
}
# endif
default:
errno = EAFNOSUPPORT;
} }
msg_Err (obj, "Multicast not supported"); #endif
msg_Err (obj, "cannot join multicast group: %m");
return -1; return -1;
} }
int net_Subscribe (vlc_object_t *obj, int fd,
const struct sockaddr *addr, socklen_t addrlen)
{
return net_SourceSubscribe (obj, fd, NULL, 0, addr, addrlen);
}
static int net_SetDSCP( int fd, uint8_t dscp ) static int net_SetDSCP( int fd, uint8_t dscp )
{ {
struct sockaddr_storage addr; struct sockaddr_storage addr;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment