Commit 75d339e0 authored by Martin Storsjö's avatar Martin Storsjö

udp: Support IGMPv3 source specific multicast and source blocking

Based on an original patch by Stephen D'Angelo <SDAngelo@evertz.com>.
Signed-off-by: 's avatarMartin Storsjö <martin@martin.st>
parent fa845061
...@@ -1136,6 +1136,8 @@ HAVE_LIST=" ...@@ -1136,6 +1136,8 @@ HAVE_LIST="
strptime strptime
strtok_r strtok_r
struct_addrinfo struct_addrinfo
struct_group_source_req
struct_ip_mreq_source
struct_ipv6_mreq struct_ipv6_mreq
struct_rusage_ru_maxrss struct_rusage_ru_maxrss
struct_sockaddr_in6 struct_sockaddr_in6
...@@ -2811,6 +2813,8 @@ fi ...@@ -2811,6 +2813,8 @@ fi
if enabled network; then if enabled network; then
check_type "sys/types.h sys/socket.h" socklen_t check_type "sys/types.h sys/socket.h" socklen_t
check_type netdb.h "struct addrinfo" check_type netdb.h "struct addrinfo"
check_type netinet/in.h "struct group_source_req" -D_BSD_SOURCE
check_type netinet/in.h "struct ip_mreq_source" -D_BSD_SOURCE
check_type netinet/in.h "struct ipv6_mreq" -D_DARWIN_C_SOURCE check_type netinet/in.h "struct ipv6_mreq" -D_DARWIN_C_SOURCE
check_type netinet/in.h "struct sockaddr_in6" check_type netinet/in.h "struct sockaddr_in6"
check_type "sys/types.h sys/socket.h" "struct sockaddr_storage" check_type "sys/types.h sys/socket.h" "struct sockaddr_storage"
...@@ -2826,6 +2830,8 @@ if enabled network; then ...@@ -2826,6 +2830,8 @@ if enabled network; then
network_extralibs="-lws2_32"; } network_extralibs="-lws2_32"; }
check_type ws2tcpip.h socklen_t check_type ws2tcpip.h socklen_t
check_type ws2tcpip.h "struct addrinfo" check_type ws2tcpip.h "struct addrinfo"
check_type ws2tcpip.h "struct group_source_req"
check_type ws2tcpip.h "struct ip_mreq_source"
check_type ws2tcpip.h "struct ipv6_mreq" check_type ws2tcpip.h "struct ipv6_mreq"
check_type ws2tcpip.h "struct sockaddr_in6" check_type ws2tcpip.h "struct sockaddr_in6"
check_type ws2tcpip.h "struct sockaddr_storage" check_type ws2tcpip.h "struct sockaddr_storage"
......
...@@ -536,6 +536,14 @@ and makes writes return with AVERROR(ECONNREFUSED) if "destination ...@@ -536,6 +536,14 @@ and makes writes return with AVERROR(ECONNREFUSED) if "destination
unreachable" is received. unreachable" is received.
For receiving, this gives the benefit of only receiving packets from For receiving, this gives the benefit of only receiving packets from
the specified peer address/port. the specified peer address/port.
@item sources=@var{address}[,@var{address}]
Only receive packets sent to the multicast group from one of the
specified sender IP addresses.
@item block=@var{address}[,@var{address}]
Ignore packets sent to the multicast group from the specified
sender IP addresses.
@end table @end table
Some usage examples of the udp protocol with @command{avconv} follow. Some usage examples of the udp protocol with @command{avconv} follow.
......
...@@ -168,6 +168,79 @@ static struct addrinfo* udp_resolve_host(const char *hostname, int port, ...@@ -168,6 +168,79 @@ static struct addrinfo* udp_resolve_host(const char *hostname, int port,
return res; return res;
} }
static int udp_set_multicast_sources(int sockfd, struct sockaddr *addr,
int addr_len, char **sources,
int nb_sources, int include)
{
#if HAVE_STRUCT_GROUP_SOURCE_REQ && defined(MCAST_BLOCK_SOURCE) && !defined(_WIN32)
/* These ones are available in the microsoft SDK, but don't seem to work
* as on linux, so just prefer the v4-only approach there for now. */
int i;
for (i = 0; i < nb_sources; i++) {
struct group_source_req mreqs;
int level = addr->sa_family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6;
struct addrinfo *sourceaddr = udp_resolve_host(sources[i], 0,
SOCK_DGRAM, AF_UNSPEC,
AI_NUMERICHOST);
if (!sourceaddr)
return AVERROR(ENOENT);
mreqs.gsr_interface = 0;
memcpy(&mreqs.gsr_group, addr, addr_len);
memcpy(&mreqs.gsr_source, sourceaddr->ai_addr, sourceaddr->ai_addrlen);
freeaddrinfo(sourceaddr);
if (setsockopt(sockfd, level,
include ? MCAST_JOIN_SOURCE_GROUP : MCAST_BLOCK_SOURCE,
(const void *)&mreqs, sizeof(mreqs)) < 0) {
if (include)
log_net_error(NULL, AV_LOG_ERROR, "setsockopt(MCAST_JOIN_SOURCE_GROUP)");
else
log_net_error(NULL, AV_LOG_ERROR, "setsockopt(MCAST_BLOCK_SOURCE)");
return ff_neterrno();
}
}
#elif HAVE_STRUCT_IP_MREQ_SOURCE && defined(IP_BLOCK_SOURCE)
int i;
if (addr->sa_family != AF_INET) {
av_log(NULL, AV_LOG_ERROR,
"Setting multicast sources only supported for IPv4\n");
return AVERROR(EINVAL);
}
for (i = 0; i < nb_sources; i++) {
struct ip_mreq_source mreqs;
struct addrinfo *sourceaddr = udp_resolve_host(sources[i], 0,
SOCK_DGRAM, AF_UNSPEC,
AI_NUMERICHOST);
if (!sourceaddr)
return AVERROR(ENOENT);
if (sourceaddr->ai_addr->sa_family != AF_INET) {
freeaddrinfo(sourceaddr);
av_log(NULL, AV_LOG_ERROR, "%s is of incorrect protocol family\n",
sources[i]);
return AVERROR(EINVAL);
}
mreqs.imr_multiaddr.s_addr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
mreqs.imr_interface.s_addr = INADDR_ANY;
mreqs.imr_sourceaddr.s_addr = ((struct sockaddr_in *)sourceaddr->ai_addr)->sin_addr.s_addr;
freeaddrinfo(sourceaddr);
if (setsockopt(sockfd, IPPROTO_IP,
include ? IP_ADD_SOURCE_MEMBERSHIP : IP_BLOCK_SOURCE,
(const void *)&mreqs, sizeof(mreqs)) < 0) {
if (include)
log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP)");
else
log_net_error(NULL, AV_LOG_ERROR, "setsockopt(IP_BLOCK_SOURCE)");
return ff_neterrno();
}
}
#else
return AVERROR(ENOSYS);
#endif
return 0;
}
static int udp_set_url(struct sockaddr_storage *addr, static int udp_set_url(struct sockaddr_storage *addr,
const char *hostname, int port) const char *hostname, int port)
{ {
...@@ -318,6 +391,8 @@ static int udp_open(URLContext *h, const char *uri, int flags) ...@@ -318,6 +391,8 @@ static int udp_open(URLContext *h, const char *uri, int flags)
struct sockaddr_storage my_addr; struct sockaddr_storage my_addr;
int len; int len;
int reuse_specified = 0; int reuse_specified = 0;
int i, include = 0, num_sources = 0;
char *sources[32];
h->is_streamed = 1; h->is_streamed = 1;
h->max_packet_size = 1472; h->max_packet_size = 1472;
...@@ -355,6 +430,25 @@ static int udp_open(URLContext *h, const char *uri, int flags) ...@@ -355,6 +430,25 @@ static int udp_open(URLContext *h, const char *uri, int flags)
if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) { if (av_find_info_tag(buf, sizeof(buf), "localaddr", p)) {
av_strlcpy(localaddr, buf, sizeof(localaddr)); av_strlcpy(localaddr, buf, sizeof(localaddr));
} }
if (av_find_info_tag(buf, sizeof(buf), "sources", p))
include = 1;
if (include || av_find_info_tag(buf, sizeof(buf), "block", p)) {
char *source_start;
source_start = buf;
while (1) {
char *next = strchr(source_start, ',');
if (next)
*next = '\0';
sources[num_sources] = av_strdup(source_start);
if (!sources[num_sources])
goto fail;
source_start = next + 1;
num_sources++;
if (num_sources >= FF_ARRAY_ELEMS(sources) || !next)
break;
}
}
} }
/* fill the dest addr */ /* fill the dest addr */
...@@ -412,8 +506,21 @@ static int udp_open(URLContext *h, const char *uri, int flags) ...@@ -412,8 +506,21 @@ static int udp_open(URLContext *h, const char *uri, int flags)
} }
if (h->flags & AVIO_FLAG_READ) { if (h->flags & AVIO_FLAG_READ) {
/* input */ /* input */
if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0) if (num_sources == 0 || !include) {
if (udp_join_multicast_group(udp_fd, (struct sockaddr *)&s->dest_addr) < 0)
goto fail;
if (num_sources) {
if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, sources, num_sources, 0) < 0)
goto fail;
}
} else if (include && num_sources) {
if (udp_set_multicast_sources(udp_fd, (struct sockaddr *)&s->dest_addr, s->dest_addr_len, sources, num_sources, 1) < 0)
goto fail;
} else {
av_log(NULL, AV_LOG_ERROR, "invalid udp settings: inclusive multicast but no sources given\n");
goto fail; goto fail;
}
} }
} }
...@@ -441,11 +548,16 @@ static int udp_open(URLContext *h, const char *uri, int flags) ...@@ -441,11 +548,16 @@ static int udp_open(URLContext *h, const char *uri, int flags)
} }
} }
for (i = 0; i < num_sources; i++)
av_free(sources[i]);
s->udp_fd = udp_fd; s->udp_fd = udp_fd;
return 0; return 0;
fail: fail:
if (udp_fd >= 0) if (udp_fd >= 0)
closesocket(udp_fd); closesocket(udp_fd);
for (i = 0; i < num_sources; i++)
av_free(sources[i]);
return AVERROR(EIO); return AVERROR(EIO);
} }
......
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