mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 17:20:36 +00:00
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:
parent
c18b609c06
commit
123d62712c
1 changed files with 388 additions and 1 deletions
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue