udpsrc: Filter out multicast packets that are not for our multicast address

https://bugzilla.gnome.org/show_bug.cgi?id=767980
This commit is contained in:
Sebastian Dröge 2016-06-23 20:21:59 +03:00
parent c18b609c06
commit 123d62712c

View file

@ -107,6 +107,12 @@
#include "config.h"
#endif
/* Needed to get struct in6_pktinfo */
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include "gstudpsrc.h"
@ -114,6 +120,272 @@
#include <gio/gnetworking.h>
/* Control messages for getting the destination address */
#ifdef IP_PKTINFO
GType gst_ip_pktinfo_message_get_type (void);
#define GST_TYPE_IP_PKTINFO_MESSAGE (gst_ip_pktinfo_message_get_type ())
#define GST_IP_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GST_TYPE_IP_PKTINFO_MESSAGE, GstIPPktinfoMessage))
#define GST_IP_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GST_TYPE_IP_PKTINFO_MESSAGE, GstIPPktinfoMessageClass))
#define GST_IS_IP_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GST_TYPE_IP_PKTINFO_MESSAGE))
#define GST_IS_IP_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GST_TYPE_IP_PKTINFO_MESSAGE))
#define GST_IP_PKTINFO_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GST_TYPE_IP_PKTINFO_MESSAGE, GstIPPktinfoMessageClass))
typedef struct _GstIPPktinfoMessage GstIPPktinfoMessage;
typedef struct _GstIPPktinfoMessageClass GstIPPktinfoMessageClass;
struct _GstIPPktinfoMessageClass
{
GSocketControlMessageClass parent_class;
};
struct _GstIPPktinfoMessage
{
GSocketControlMessage parent;
guint ifindex;
struct in_addr spec_dst, addr;
};
G_DEFINE_TYPE (GstIPPktinfoMessage, gst_ip_pktinfo_message,
G_TYPE_SOCKET_CONTROL_MESSAGE);
static gsize
gst_ip_pktinfo_message_get_size (GSocketControlMessage * message)
{
return sizeof (struct in_pktinfo);
}
static int
gst_ip_pktinfo_message_get_level (GSocketControlMessage * message)
{
return IPPROTO_IP;
}
static int
gst_ip_pktinfo_message_get_msg_type (GSocketControlMessage * message)
{
return IP_PKTINFO;
}
static GSocketControlMessage *
gst_ip_pktinfo_message_deserialize (gint level,
gint type, gsize size, gpointer data)
{
struct in_pktinfo *pktinfo;
GstIPPktinfoMessage *message;
if (level != IPPROTO_IP || type != IP_PKTINFO)
return NULL;
if (size < sizeof (struct in_pktinfo))
return NULL;
pktinfo = data;
message = g_object_new (GST_TYPE_IP_PKTINFO_MESSAGE, NULL);
message->ifindex = pktinfo->ipi_ifindex;
message->spec_dst = pktinfo->ipi_spec_dst;
message->addr = pktinfo->ipi_addr;
return G_SOCKET_CONTROL_MESSAGE (message);
}
static void
gst_ip_pktinfo_message_init (GstIPPktinfoMessage * message)
{
}
static void
gst_ip_pktinfo_message_class_init (GstIPPktinfoMessageClass * class)
{
GSocketControlMessageClass *scm_class;
scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
scm_class->get_size = gst_ip_pktinfo_message_get_size;
scm_class->get_level = gst_ip_pktinfo_message_get_level;
scm_class->get_type = gst_ip_pktinfo_message_get_msg_type;
scm_class->deserialize = gst_ip_pktinfo_message_deserialize;
}
#endif
#ifdef IPV6_PKTINFO
GType gst_ipv6_pktinfo_message_get_type (void);
#define GST_TYPE_IPV6_PKTINFO_MESSAGE (gst_ipv6_pktinfo_message_get_type ())
#define GST_IPV6_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GST_TYPE_IPV6_PKTINFO_MESSAGE, GstIPV6PktinfoMessage))
#define GST_IPV6_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GST_TYPE_IPV6_PKTINFO_MESSAGE, GstIPV6PktinfoMessageClass))
#define GST_IS_IPV6_PKTINFO_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GST_TYPE_IPV6_PKTINFO_MESSAGE))
#define GST_IS_IPV6_PKTINFO_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GST_TYPE_IPV6_PKTINFO_MESSAGE))
#define GST_IPV6_PKTINFO_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GST_TYPE_IPV6_PKTINFO_MESSAGE, GstIPV6PktinfoMessageClass))
typedef struct _GstIPV6PktinfoMessage GstIPV6PktinfoMessage;
typedef struct _GstIPV6PktinfoMessageClass GstIPV6PktinfoMessageClass;
struct _GstIPV6PktinfoMessageClass
{
GSocketControlMessageClass parent_class;
};
struct _GstIPV6PktinfoMessage
{
GSocketControlMessage parent;
guint ifindex;
struct in6_addr addr;
};
G_DEFINE_TYPE (GstIPV6PktinfoMessage, gst_ipv6_pktinfo_message,
G_TYPE_SOCKET_CONTROL_MESSAGE);
static gsize
gst_ipv6_pktinfo_message_get_size (GSocketControlMessage * message)
{
return sizeof (struct in6_pktinfo);
}
static int
gst_ipv6_pktinfo_message_get_level (GSocketControlMessage * message)
{
return IPPROTO_IPV6;
}
static int
gst_ipv6_pktinfo_message_get_msg_type (GSocketControlMessage * message)
{
return IPV6_PKTINFO;
}
static GSocketControlMessage *
gst_ipv6_pktinfo_message_deserialize (gint level,
gint type, gsize size, gpointer data)
{
struct in6_pktinfo *pktinfo;
GstIPV6PktinfoMessage *message;
if (level != IPPROTO_IPV6 || type != IPV6_PKTINFO)
return NULL;
if (size < sizeof (struct in_pktinfo))
return NULL;
pktinfo = data;
message = g_object_new (GST_TYPE_IPV6_PKTINFO_MESSAGE, NULL);
message->ifindex = pktinfo->ipi6_ifindex;
message->addr = pktinfo->ipi6_addr;
return G_SOCKET_CONTROL_MESSAGE (message);
}
static void
gst_ipv6_pktinfo_message_init (GstIPV6PktinfoMessage * message)
{
}
static void
gst_ipv6_pktinfo_message_class_init (GstIPV6PktinfoMessageClass * class)
{
GSocketControlMessageClass *scm_class;
scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
scm_class->get_size = gst_ipv6_pktinfo_message_get_size;
scm_class->get_level = gst_ipv6_pktinfo_message_get_level;
scm_class->get_type = gst_ipv6_pktinfo_message_get_msg_type;
scm_class->deserialize = gst_ipv6_pktinfo_message_deserialize;
}
#endif
#ifdef IP_RECVDSTADDR
GType gst_ip_recvdstaddr_message_get_type (void);
#define GST_TYPE_IP_RECVDSTADDR_MESSAGE (gst_ip_recvdstaddr_message_get_type ())
#define GST_IP_RECVDSTADDR_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GST_TYPE_IP_RECVDSTADDR_MESSAGE, GstIPRecvdstaddrMessage))
#define GST_IP_RECVDSTADDR_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GST_TYPE_IP_RECVDSTADDR_MESSAGE, GstIPRecvdstaddrMessageClass))
#define GST_IS_IP_RECVDSTADDR_MESSAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GST_TYPE_IP_RECVDSTADDR_MESSAGE))
#define GST_IS_IP_RECVDSTADDR_MESSAGE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GST_TYPE_IP_RECVDSTADDR_MESSAGE))
#define GST_IP_RECVDSTADDR_MESSAGE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GST_TYPE_IP_RECVDSTADDR_MESSAGE, GstIPRecvdstaddrMessageClass))
typedef struct _GstIPRecvdstaddrMessage GstIPRecvdstaddrMessage;
typedef struct _GstIPRecvdstaddrMessageClass GstIPRecvdstaddrMessageClass;
struct _GstIPRecvdstaddrMessageClass
{
GSocketControlMessageClass parent_class;
};
struct _GstIPRecvdstaddrMessage
{
GSocketControlMessage parent;
guint ifindex;
struct in_addr addr;
};
G_DEFINE_TYPE (GstIPRecvdstaddrMessage, gst_ip_recvdstaddr_message,
G_TYPE_SOCKET_CONTROL_MESSAGE);
static gsize
gst_ip_recvdstaddr_message_get_size (GSocketControlMessage * message)
{
return sizeof (struct in_addr);
}
static int
gst_ip_recvdstaddr_message_get_level (GSocketControlMessage * message)
{
return IPPROTO_IP;
}
static int
gst_ip_recvdstaddr_message_get_msg_type (GSocketControlMessage * message)
{
return IP_RECVDSTADDR;
}
static GSocketControlMessage *
gst_ip_recvdstaddr_message_deserialize (gint level,
gint type, gsize size, gpointer data)
{
struct in_addr *addr;
GstIPRecvdstaddrMessage *message;
if (level != IPPROTO_IP || type != IP_RECVDSTADDR)
return NULL;
if (size < sizeof (struct in_addr))
return NULL;
addr = data;
message = g_object_new (GST_TYPE_IP_RECVDSTADDR_MESSAGE, NULL);
message->addr = g_inet_address_new_from_bytes ((guint8 *) addr, AF_INET);
return G_SOCKET_CONTROL_MESSAGE (message);
}
static void
gst_ip_recvdstaddr_message_init (GstIPRecvdstaddrMessage * message)
{
}
static void
gst_ip_recvdstaddr_message_class_init (GstIPRecvdstaddrMessageClass * class)
{
GSocketControlMessageClass *scm_class;
scm_class = G_SOCKET_CONTROL_MESSAGE_CLASS (class);
scm_class->get_size = gst_ip_recvdstaddr_message_get_size;
scm_class->get_level = gst_ip_recvdstaddr_message_get_level;
scm_class->get_type = gst_ip_recvdstaddr_message_get_msg_type;
scm_class->deserialize = gst_ip_recvdstaddr_message_deserialize;
}
#endif
/* not 100% correct, but a good upper bound for memory allocation purposes */
#define MAX_IPV4_UDP_PACKET_SIZE (65536 - 8)
@ -201,6 +473,16 @@ gst_udpsrc_class_init (GstUDPSrcClass * klass)
GST_DEBUG_CATEGORY_INIT (udpsrc_debug, "udpsrc", 0, "UDP src");
#ifdef IP_PKTINFO
GST_TYPE_IP_PKTINFO_MESSAGE;
#endif
#ifdef IPV6_PKTINFO
GST_TYPE_IPV6_PKTINFO_MESSAGE;
#endif
#ifdef IP_RECVDSTADDR
GST_TYPE_IP_RECVDSTADDR_MESSAGE;
#endif
gobject_class->set_property = gst_udpsrc_set_property;
gobject_class->get_property = gst_udpsrc_get_property;
gobject_class->finalize = gst_udpsrc_finalize;
@ -543,6 +825,8 @@ gst_udpsrc_create (GstPushSrc * psrc, GstBuffer ** buf)
GError *err = NULL;
gssize res;
gsize offset;
GSocketControlMessage **msgs = NULL;
gint n_msgs = 0, i;
udpsrc = GST_UDPSRC_CAST (psrc);
@ -593,7 +877,7 @@ retry:
res =
g_socket_receive_message (udpsrc->used_socket, p_saddr, udpsrc->vec, 2,
NULL, NULL, &flags, udpsrc->cancellable, &err);
&msgs, &n_msgs, &flags, udpsrc->cancellable, &err);
if (G_UNLIKELY (res < 0)) {
/* G_IO_ERROR_HOST_UNREACHABLE for a UDP socket means that a packet sent
@ -616,6 +900,66 @@ retry:
if (res > udpsrc->max_size)
udpsrc->max_size = res;
/* Retry if multicast and the destination address is not ours. We don't want
* to receive arbitrary packets */
{
GInetAddress *iaddr = g_inet_socket_address_get_address (udpsrc->addr);
gboolean skip_packet = FALSE;
gsize iaddr_size = g_inet_address_get_native_size (iaddr);
const guint8 *iaddr_bytes = g_inet_address_to_bytes (iaddr);
if (g_inet_address_get_is_multicast (iaddr)) {
for (i = 0; i < n_msgs && !skip_packet; i++) {
#ifdef IP_PKTINFO
if (GST_IS_IP_PKTINFO_MESSAGE (msgs[i])) {
GstIPPktinfoMessage *msg = GST_IP_PKTINFO_MESSAGE (msgs[i]);
if (sizeof (msg->addr) == iaddr_size
&& memcmp (iaddr_bytes, &msg->addr, sizeof (msg->addr)))
skip_packet = TRUE;
}
#endif
#ifdef IPV6_PKTINFO
if (GST_IS_IPV6_PKTINFO_MESSAGE (msgs[i])) {
GstIPV6PktinfoMessage *msg = GST_IPV6_PKTINFO_MESSAGE (msgs[i]);
if (sizeof (msg->addr) == iaddr_size
&& memcmp (iaddr_bytes, &msg->addr, sizeof (msg->addr)))
skip_packet = TRUE;
}
#endif
#ifdef IP_RECVDSTADDR
if (GST_IS_IP_RECVDSTADDR_MESSAGE (msgs[i])) {
GstIPRecvdstaddrMessage *msg = GST_IP_RECVDSTADDR_MESSAGE (msgs[i]);
if (sizeof (msg->addr) == iaddr_size
&& memcmp (iaddr_bytes, &msg->addr, sizeof (msg->addr)))
skip_packet = TRUE;
}
#endif
}
}
for (i = 0; i < n_msgs; i++) {
g_object_unref (msgs[i]);
}
g_free (msgs);
if (skip_packet) {
GST_DEBUG_OBJECT (udpsrc,
"Dropping packet for a different multicast address");
if (saddr != NULL) {
g_object_unref (saddr);
saddr = NULL;
}
goto retry;
}
}
outbuf = gst_buffer_new ();
/* append first memory chunk to buffer */
@ -1075,6 +1419,49 @@ gst_udpsrc_open (GstUDPSrc * src)
g_inet_socket_address_get_address (src->addr),
FALSE, src->multi_iface, &err))
goto membership;
if (g_inet_address_get_family (g_inet_socket_address_get_address
(src->addr)) == G_SOCKET_FAMILY_IPV4) {
#if defined(IP_PKTINFO)
if (!g_socket_set_option (src->used_socket, IPPROTO_IP, IP_PKTINFO, TRUE,
&err)) {
GST_WARNING_OBJECT (src, "Failed to enable IP_PKTINFO: %s",
err->message);
g_clear_error (&err);
}
#elif defined(IP_RECVDSTADDR)
if (!g_socket_set_option (src->used_socket, IPPROTO_IP, IP_RECVDSTADDR,
TRUE, &err)) {
GST_WARNING_OBJECT (src, "Failed to enable IP_RECVDSTADDR: %s",
err->message);
g_clear_error (&err);
}
#else
#pragma message("No API available for getting IPv4 destination address")
GST_WARNING_OBJECT (src, "No API available for getting IPv4 destination "
"address, will receive packets for every destination to our port");
#endif
} else
if (g_inet_address_get_family (g_inet_socket_address_get_address
(src->addr)) == G_SOCKET_FAMILY_IPV6) {
#ifdef IPV6_PKTINFO
#ifdef IPV6_RECVPKTINFO
if (!g_socket_set_option (src->used_socket, IPPROTO_IPV6,
IPV6_RECVPKTINFO, TRUE, &err)) {
#else
if (!g_socket_set_option (src->used_socket, IPPROTO_IPV6, IPV6_PKTINFO,
TRUE, &err)) {
#endif
GST_WARNING_OBJECT (src, "Failed to enable IPV6_PKTINFO: %s",
err->message);
g_clear_error (&err);
}
#else
#pragma message("No API available for getting IPv6 destination address")
GST_WARNING_OBJECT (src, "No API available for getting IPv6 destination "
"address, will receive packets for every destination to our port");
#endif
}
}
/* NOTE: sockaddr_in.sin_port works for ipv4 and ipv6 because sin_port