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 char *ifname = var_InheritString (obj, "miface");
union if (ifname == NULL)
{
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; return 0;
error: unsigned ifindex = if_nametoindex (ifname);
#endif if (ifindex == 0)
msg_Err (obj, "invalid multicast interface: %s", ifname);
msg_Err (obj, "cannot join IPv4 multicast group (%m)"); free (ifname);
return -1; return ifindex;
} }
static int
net_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src)
{
#ifdef IPV6_JOIN_GROUP
struct ipv6_mreq gr6;
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;
#else
errno = ENOSYS;
#endif
msg_Err (obj, "cannot join IPv6 any-source multicast group (%m)");
return -1;
}
#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));
memcpy (&gsr.gsr_group, grp, grplen);
if (setsockopt (fd, level, MCAST_JOIN_SOURCE_GROUP,
&gsr, sizeof (gsr)) == 0)
return 0;
#else
if (src->sa_family != grp->sa_family)
{ {
#ifdef AF_INET6 errno = EAFNOSUPPORT;
case AF_INET6: return -1;
if (memcmp (&((const struct sockaddr_in6 *)src)->sin6_addr, }
&in6addr_any, sizeof (in6addr_any)) == 0)
src = NULL;
break;
#endif
switch (grp->sa_family)
{
# ifdef IP_ADD_SOURCE_MEMBERSHIP
/* IPv4-specific API */
case AF_INET: case AF_INET:
if (((const struct sockaddr_in *)src)->sin_addr.s_addr {
== INADDR_ANY) struct ip_mreq_source imr;
src = NULL;
memset (&imr, 0, sizeof (imr));
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; break;
} }
# endif
default:
errno = EAFNOSUPPORT;
}
#endif
msg_Err (obj, "cannot join source multicast group: %m");
msg_Warn (obj, "trying ASM instead of SSM...");
return net_Subscribe (obj, fd, grp, grplen);
}
/* Agnostic ASM/SSM multicast join */
#ifdef MCAST_JOIN_SOURCE_GROUP int net_Subscribe (vlc_object_t *obj, int fd,
union const struct sockaddr *grp, socklen_t grplen)
{ {
#ifdef MCAST_JOIN_GROUP
/* Agnostic SSM multicast join */
int level;
struct group_req gr; struct group_req gr;
struct group_source_req gsr;
} opt;
socklen_t optlen;
memset (&opt, 0, sizeof (opt)); memset (&gr, 0, sizeof (gr));
gr.gr_interface = var_GetIfIndex (obj);
if (src != NULL) switch (grp->sa_family)
{ {
if ((grplen > sizeof (opt.gsr.gsr_group)) #ifdef AF_INET6
|| (srclen > sizeof (opt.gsr.gsr_source))) case AF_INET6:
return -1; {
const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
opt.gsr.gsr_interface = iid; level = SOL_IPV6;
memcpy (&opt.gsr.gsr_source, src, srclen); assert (grplen >= sizeof (struct sockaddr_in6));
memcpy (&opt.gsr.gsr_group, grp, grplen); if (g6->sin6_scope_id != 0)
optlen = sizeof (opt.gsr); gr.gr_interface = g6->sin6_scope_id;
break;
} }
else #endif
{ case AF_INET:
if (grplen > sizeof (opt.gr.gr_group)) level = SOL_IP;
break;
default:
errno = EAFNOSUPPORT;
return -1; return -1;
opt.gr.gr_interface = iid;
memcpy (&opt.gr.gr_group, grp, grplen);
optlen = sizeof (opt.gr);
} }
msg_Dbg (obj, "Multicast %sgroup join request", src ? "source " : ""); assert (grplen <= sizeof (gr.gr_group));
memcpy (&gr.gr_group, grp, grplen);
if (setsockopt (fd, level, if (setsockopt (fd, level, MCAST_JOIN_GROUP, &gr, sizeof (gr)) == 0)
src ? MCAST_JOIN_SOURCE_GROUP : MCAST_JOIN_GROUP,
(void *)&opt, optlen) == 0)
return 0; return 0;
#endif
/* Fallback to IPv-specific APIs */
if ((src != NULL) && (src->sa_family != grp->sa_family))
return -1;
#else
switch (grp->sa_family) switch (grp->sa_family)
{ {
case AF_INET: # ifdef IPV6_JOIN_GROUP
if ((grplen < sizeof (struct sockaddr_in)) case AF_INET6:
|| ((src != NULL) && (srclen < sizeof (struct sockaddr_in)))) {
return -1; struct ipv6_mreq ipv6mr;
const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
if (net_IPv4Join (obj, fd, (const struct sockaddr_in *)src, memset (&ipv6mr, 0, sizeof (ipv6mr));
(const struct sockaddr_in *)grp) == 0) 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; return 0;
break; break;
}
# endif
# ifdef IP_ADD_MEMBERSHIP
case AF_INET:
{
struct ip_mreq imr;
#ifdef AF_INET6 memset (&imr, 0, sizeof (imr));
case AF_INET6: assert (grplen >= sizeof (struct sockaddr_in));
if ((grplen < sizeof (struct sockaddr_in6)) imr.imr_multiaddr = ((const struct sockaddr_in *)grp)->sin_addr;
|| ((src != NULL) && (srclen < sizeof (struct sockaddr_in6)))) if (setsockopt (fd, SOL_IP, IP_ADD_MEMBERSHIP,
return -1; &imr, sizeof (imr)) == 0)
/* 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)
return 0; return 0;
break; break;
#endif
} }
# endif
msg_Err (obj, "Multicast group join error (%m)"); default:
errno = EAFNOSUPPORT;
if (src != NULL)
{
msg_Warn (obj, "Trying ASM instead of SSM...");
return net_Subscribe (obj, fd, grp, grplen);
} }
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