From 336353deaad6e4da28665f50a36988b02b55c20c Mon Sep 17 00:00:00 2001 From: Ed Torbett Date: Thu, 18 Jul 2013 20:04:16 +0300 Subject: [PATCH 1/3] rtpproto: Support IGMPv3 source specific multicast inclusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blocking/exclusion is not supported yet. The rtp protocol parameter takes the same form as the existing sources parameter for the udp protocol. Signed-off-by: Martin Storsjö --- libavformat/rtpproto.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 190dff4bd2..20ffdab491 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -99,7 +99,8 @@ static av_printf_format(3, 4) void url_add_option(char *buf, int buf_size, const static void build_udp_url(char *buf, int buf_size, const char *hostname, int port, int local_port, int ttl, - int max_packet_size, int connect) + int max_packet_size, int connect, + const char* sources) { ff_url_join(buf, buf_size, "udp", NULL, hostname, port, NULL); if (local_port >= 0) @@ -110,6 +111,8 @@ static void build_udp_url(char *buf, int buf_size, url_add_option(buf, buf_size, "pkt_size=%d", max_packet_size); if (connect) url_add_option(buf, buf_size, "connect=1"); + if (sources && sources[0]) + url_add_option(buf, buf_size, "sources=%s", sources); } /** @@ -122,6 +125,7 @@ static void build_udp_url(char *buf, int buf_size, * 'connect=0/1' : do a connect() on the UDP socket * deprecated option: * 'localport=n' : set the local port to n + * 'sources=ip[,ip]' : list allowed source IP addresses * * if rtcpport isn't set the rtcp port will be the rtp port + 1 * if local rtp port isn't set any available port will be used for the local @@ -135,7 +139,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags) int rtp_port, rtcp_port, ttl, connect, local_rtp_port, local_rtcp_port, max_packet_size; - char hostname[256]; + char hostname[256], sources[1024] = ""; char buf[1024]; char path[1024]; const char *p; @@ -173,11 +177,14 @@ static int rtp_open(URLContext *h, const char *uri, int flags) if (av_find_info_tag(buf, sizeof(buf), "connect", p)) { connect = strtol(buf, NULL, 10); } + if (av_find_info_tag(buf, sizeof(buf), "sources", p)) { + av_strlcpy(sources, buf, sizeof(sources)); + } } build_udp_url(buf, sizeof(buf), hostname, rtp_port, local_rtp_port, ttl, max_packet_size, - connect); + connect, sources); if (ffurl_open(&s->rtp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) goto fail; if (local_rtp_port>=0 && local_rtcp_port<0) @@ -185,7 +192,7 @@ static int rtp_open(URLContext *h, const char *uri, int flags) build_udp_url(buf, sizeof(buf), hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size, - connect); + connect, sources); if (ffurl_open(&s->rtcp_hd, buf, flags, &h->interrupt_callback, NULL) < 0) goto fail; From 4d97ca040b40eb4771d1a6cacdb611f61a53afd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Thu, 18 Jul 2013 21:12:14 +0300 Subject: [PATCH 2/3] rtpproto: Check the source IP if one single source has been specified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If another peer is sending unicast packets to the same port that we are listening on, those packets can end up being received despite using source specific multicast. For those cases, manually check the source address of received packets against the intended source address. This only handles the case when the source list is one single IP address for now, which probably is the most common case. Based on a patch by Ed Torbett. Signed-off-by: Martin Storsjö --- libavformat/rtpproto.c | 58 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/libavformat/rtpproto.c b/libavformat/rtpproto.c index 20ffdab491..b050382301 100644 --- a/libavformat/rtpproto.c +++ b/libavformat/rtpproto.c @@ -42,7 +42,8 @@ typedef struct RTPContext { URLContext *rtp_hd, *rtcp_hd; - int rtp_fd, rtcp_fd; + int rtp_fd, rtcp_fd, ssm; + struct sockaddr_storage ssm_addr; } RTPContext; /** @@ -75,6 +76,44 @@ int ff_rtp_set_remote_url(URLContext *h, const char *uri) return 0; } +static struct addrinfo* rtp_resolve_host(const char *hostname, int port, + int type, int family, int flags) +{ + struct addrinfo hints = { 0 }, *res = 0; + int error; + char service[16]; + + snprintf(service, sizeof(service), "%d", port); + hints.ai_socktype = type; + hints.ai_family = family; + hints.ai_flags = flags; + if ((error = getaddrinfo(hostname, service, &hints, &res))) { + res = NULL; + av_log(NULL, AV_LOG_ERROR, "rtp_resolve_host: %s\n", gai_strerror(error)); + } + + return res; +} + +static int compare_addr(const struct sockaddr_storage *a, + const struct sockaddr_storage *b) +{ + if (a->ss_family != b->ss_family) + return 1; + if (a->ss_family == AF_INET) { + return (((const struct sockaddr_in *)a)->sin_addr.s_addr != + ((const struct sockaddr_in *)b)->sin_addr.s_addr); + } + +#if defined(IPPROTO_IPV6) + if (a->ss_family == AF_INET6) { + const uint8_t *s6_addr_a = ((const struct sockaddr_in6 *)a)->sin6_addr.s6_addr; + const uint8_t *s6_addr_b = ((const struct sockaddr_in6 *)b)->sin6_addr.s6_addr; + return memcmp(s6_addr_a, s6_addr_b, 16); + } +#endif + return 1; +} /** * add option to url of the form: @@ -178,7 +217,20 @@ static int rtp_open(URLContext *h, const char *uri, int flags) connect = strtol(buf, NULL, 10); } if (av_find_info_tag(buf, sizeof(buf), "sources", p)) { + struct addrinfo *sourceaddr = NULL; av_strlcpy(sources, buf, sizeof(sources)); + + /* Try resolving the IP if only one IP is specified - we don't + * support manually checking more than one IP. */ + if (!strchr(sources, ',')) + sourceaddr = rtp_resolve_host(sources, 0, + SOCK_DGRAM, AF_UNSPEC, + AI_NUMERICHOST); + if (sourceaddr) { + s->ssm = 1; + memcpy(&s->ssm_addr, sourceaddr->ai_addr, sourceaddr->ai_addrlen); + freeaddrinfo(sourceaddr); + } } } @@ -238,6 +290,8 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size) continue; return AVERROR(EIO); } + if (s->ssm && compare_addr(&from, &s->ssm_addr)) + continue; break; } /* then RTP */ @@ -251,6 +305,8 @@ static int rtp_read(URLContext *h, uint8_t *buf, int size) continue; return AVERROR(EIO); } + if (s->ssm && compare_addr(&from, &s->ssm_addr)) + continue; break; } } else if (n < 0) { From 36fb0d02a1faa11eaee51de01fb4061ad6092af9 Mon Sep 17 00:00:00 2001 From: Ed Torbett Date: Thu, 27 Jun 2013 08:53:00 +0100 Subject: [PATCH 3/3] rtsp: Support multicast source filters (RFC 4570) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This supports inclusion of one single IP address for now, at the media level. Specifying the filter at the session level (instead of at the media level), multiple source addresses, exclusion, or using FQDNs instead of plain IP addresses is not supported (yet at least). Signed-off-by: Martin Storsjö --- libavformat/rtsp.c | 18 ++++++++++++++++++ libavformat/rtsp.h | 1 + 2 files changed, 19 insertions(+) diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 29ef403a27..7217640e24 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -495,6 +495,22 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, p += strspn(p, SPACE_CHARS); if (av_strstart(p, "inline:", &p)) get_word(rtsp_st->crypto_params, sizeof(rtsp_st->crypto_params), &p); + } else if (av_strstart(p, "source-filter:", &p) && s->nb_streams > 0) { + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "incl")) + return; + + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "IN") != 0) + return; + get_word(buf1, sizeof(buf1), &p); + if (strcmp(buf1, "IP4") && strcmp(buf1, "IP6")) + return; + // not checking that the destination address actually matches + get_word(buf1, sizeof(buf1), &p); + + rtsp_st = rt->rtsp_streams[rt->nb_rtsp_streams - 1]; + get_word(rtsp_st->source_addr, sizeof(rtsp_st->source_addr), &p); } else { if (rt->server_type == RTSP_SERVER_WMS) ff_wms_parse_sdp_a_line(s, p); @@ -2085,6 +2101,8 @@ static int sdp_read_header(AVFormatContext *s) "?localport=%d&ttl=%d&connect=%d", rtsp_st->sdp_port, rtsp_st->sdp_ttl, rt->rtsp_flags & RTSP_FLAG_FILTER_SRC ? 1 : 0); + if (rtsp_st->source_addr[0]) + av_strlcatf(url, sizeof(url), "&sources=%s", rtsp_st->source_addr); if (ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, &s->interrupt_callback, NULL) < 0) { err = AVERROR_INVALIDDATA; diff --git a/libavformat/rtsp.h b/libavformat/rtsp.h index 44240c1d0f..eff5aa4db7 100644 --- a/libavformat/rtsp.h +++ b/libavformat/rtsp.h @@ -425,6 +425,7 @@ typedef struct RTSPStream { //@{ int sdp_port; /**< port (from SDP content) */ struct sockaddr_storage sdp_ip; /**< IP address (from SDP content) */ + char source_addr[100]; /**< Source-specific multicast source IP address (from SDP content) */ int sdp_ttl; /**< IP Time-To-Live (from SDP content) */ int sdp_payload_type; /**< payload type */ //@}