2 * Copyright 2007 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-pcapparse
23 * Extracts payloads from Ethernet-encapsulated IP packets.
24 * Use #GstPcapParse:src-ip, #GstPcapParse:dst-ip,
25 * #GstPcapParse:src-port and #GstPcapParse:dst-port to restrict which packets
29 * <title>Example pipelines</title>
31 * gst-launch-0.10 filesrc location=h264crasher.pcap ! pcapparse ! rtph264depay
32 * ! ffdec_h264 ! fakesink
33 * ]| Read from a pcap dump file using filesrc, extract the raw UDP packets,
34 * depayload and decode them.
39 * - React on state-change and update state accordingly.
40 * - Implement support for timestamping the buffers.
47 #include "gstpcapparse.h"
52 #include <arpa/inet.h>
53 #include <netinet/in.h>
71 GST_DEBUG_CATEGORY_STATIC (gst_pcap_parse_debug);
72 #define GST_CAT_DEFAULT gst_pcap_parse_debug
74 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
77 GST_STATIC_CAPS ("raw/x-pcap"));
79 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
84 static void gst_pcap_parse_finalize (GObject * object);
85 static void gst_pcap_parse_get_property (GObject * object, guint prop_id,
86 GValue * value, GParamSpec * pspec);
87 static void gst_pcap_parse_set_property (GObject * object, guint prop_id,
88 const GValue * value, GParamSpec * pspec);
90 static void gst_pcap_parse_reset (GstPcapParse * self);
92 static GstFlowReturn gst_pcap_parse_chain (GstPad * pad,
93 GstObject * parent, GstBuffer * buffer);
94 static gboolean gst_pcap_sink_event (GstPad * pad,
95 GstObject * parent, GstEvent * event);
97 #define parent_class gst_pcap_parse_parent_class
98 G_DEFINE_TYPE (GstPcapParse, gst_pcap_parse, GST_TYPE_ELEMENT);
101 gst_pcap_parse_class_init (GstPcapParseClass * klass)
103 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
104 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
106 gobject_class->finalize = gst_pcap_parse_finalize;
107 gobject_class->get_property = gst_pcap_parse_get_property;
108 gobject_class->set_property = gst_pcap_parse_set_property;
110 g_object_class_install_property (gobject_class,
111 PROP_SRC_IP, g_param_spec_string ("src-ip", "Source IP",
112 "Source IP to restrict to", "",
113 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
115 g_object_class_install_property (gobject_class,
116 PROP_DST_IP, g_param_spec_string ("dst-ip", "Destination IP",
117 "Destination IP to restrict to", "",
118 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
120 g_object_class_install_property (gobject_class,
121 PROP_SRC_PORT, g_param_spec_int ("src-port", "Source port",
122 "Source port to restrict to", -1, G_MAXUINT16, -1,
123 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
125 g_object_class_install_property (gobject_class,
126 PROP_DST_PORT, g_param_spec_int ("dst-port", "Destination port",
127 "Destination port to restrict to", -1, G_MAXUINT16, -1,
128 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
130 g_object_class_install_property (gobject_class, PROP_CAPS,
131 g_param_spec_boxed ("caps", "Caps",
132 "The caps of the source pad", GST_TYPE_CAPS,
133 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
135 g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
136 g_param_spec_int64 ("ts-offset", "Timestamp Offset",
137 "Relative timestamp offset (ns) to apply (-1 = use absolute packet time)",
138 -1, G_MAXINT64, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
140 gst_element_class_add_pad_template (element_class,
141 gst_static_pad_template_get (&sink_template));
142 gst_element_class_add_pad_template (element_class,
143 gst_static_pad_template_get (&src_template));
145 gst_element_class_set_details_simple (element_class, "PCapParse",
147 "Parses a raw pcap stream",
148 "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>");
150 GST_DEBUG_CATEGORY_INIT (gst_pcap_parse_debug, "pcapparse", 0, "pcap parser");
154 gst_pcap_parse_init (GstPcapParse * self)
156 self->sink_pad = gst_pad_new_from_static_template (&sink_template, "sink");
157 gst_pad_set_chain_function (self->sink_pad,
158 GST_DEBUG_FUNCPTR (gst_pcap_parse_chain));
159 gst_pad_use_fixed_caps (self->sink_pad);
160 gst_pad_set_event_function (self->sink_pad,
161 GST_DEBUG_FUNCPTR (gst_pcap_sink_event));
162 gst_element_add_pad (GST_ELEMENT (self), self->sink_pad);
164 self->src_pad = gst_pad_new_from_static_template (&src_template, "src");
165 gst_pad_use_fixed_caps (self->src_pad);
166 gst_element_add_pad (GST_ELEMENT (self), self->src_pad);
174 self->adapter = gst_adapter_new ();
176 gst_pcap_parse_reset (self);
180 gst_pcap_parse_finalize (GObject * object)
182 GstPcapParse *self = GST_PCAP_PARSE (object);
184 g_object_unref (self->adapter);
186 gst_caps_unref (self->caps);
188 G_OBJECT_CLASS (parent_class)->finalize (object);
192 get_ip_address_as_string (gint64 ip_addr)
196 addr.s_addr = ip_addr;
197 return inet_ntoa (addr);
204 set_ip_address_from_string (gint64 * ip_addr, const gchar * ip_str)
206 if (ip_str[0] != '\0') {
207 gulong addr = inet_addr (ip_str);
208 if (addr != INADDR_NONE)
216 gst_pcap_parse_get_property (GObject * object, guint prop_id,
217 GValue * value, GParamSpec * pspec)
219 GstPcapParse *self = GST_PCAP_PARSE (object);
223 g_value_set_string (value, get_ip_address_as_string (self->src_ip));
227 g_value_set_string (value, get_ip_address_as_string (self->dst_ip));
231 g_value_set_int (value, self->src_port);
235 g_value_set_int (value, self->dst_port);
239 gst_value_set_caps (value, self->caps);
243 g_value_set_int64 (value, self->offset);
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253 gst_pcap_parse_set_property (GObject * object, guint prop_id,
254 const GValue * value, GParamSpec * pspec)
256 GstPcapParse *self = GST_PCAP_PARSE (object);
260 set_ip_address_from_string (&self->src_ip, g_value_get_string (value));
264 set_ip_address_from_string (&self->dst_ip, g_value_get_string (value));
268 self->src_port = g_value_get_int (value);
272 self->dst_port = g_value_get_int (value);
277 const GstCaps *new_caps_val;
278 GstCaps *new_caps, *old_caps;
280 new_caps_val = gst_value_get_caps (value);
281 if (new_caps_val == NULL) {
282 new_caps = gst_caps_new_any ();
284 new_caps = gst_caps_copy (new_caps_val);
287 old_caps = self->caps;
288 self->caps = new_caps;
290 gst_caps_unref (old_caps);
292 gst_pad_set_caps (self->src_pad, new_caps);
297 self->offset = g_value_get_int64 (value);
301 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307 gst_pcap_parse_reset (GstPcapParse * self)
309 self->initialized = FALSE;
310 self->swap_endian = FALSE;
311 self->cur_packet_size = -1;
312 self->buffer_offset = 0;
313 self->cur_ts = GST_CLOCK_TIME_NONE;
314 self->base_ts = GST_CLOCK_TIME_NONE;
315 self->newsegment_sent = FALSE;
317 gst_adapter_clear (self->adapter);
321 gst_pcap_parse_read_uint32 (GstPcapParse * self, const guint8 * p)
323 guint32 val = *((guint32 *) p);
325 if (self->swap_endian) {
326 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
327 return GUINT32_FROM_BE (val);
329 return GUINT32_FROM_LE (val);
336 #define ETH_HEADER_LEN 14
337 #define SLL_HEADER_LEN 16
338 #define IP_HEADER_MIN_LEN 20
339 #define UDP_HEADER_LEN 8
341 #define IP_PROTO_UDP 17
342 #define IP_PROTO_TCP 6
346 gst_pcap_parse_scan_frame (GstPcapParse * self,
348 gint buf_size, const guint8 ** payload, gint * payload_size)
350 const guint8 *buf_ip = 0;
351 const guint8 *buf_proto;
354 guint8 ip_header_size;
362 switch (self->linktype) {
364 if (buf_size < ETH_HEADER_LEN + IP_HEADER_MIN_LEN + UDP_HEADER_LEN)
367 eth_type = GUINT16_FROM_BE (*((guint16 *) (buf + 12)));
368 buf_ip = buf + ETH_HEADER_LEN;
371 if (buf_size < SLL_HEADER_LEN + IP_HEADER_MIN_LEN + UDP_HEADER_LEN)
374 eth_type = GUINT16_FROM_BE (*((guint16 *) (buf + 14)));
375 buf_ip = buf + SLL_HEADER_LEN;
381 if (eth_type != 0x800)
385 if (((b >> 4) & 0x0f) != 4)
388 ip_header_size = (b & 0x0f) * 4;
389 if (buf_ip + ip_header_size > buf + buf_size)
392 ip_protocol = *(buf_ip + 9);
393 GST_LOG_OBJECT (self, "ip proto %d", (gint) ip_protocol);
395 if (ip_protocol != IP_PROTO_UDP && ip_protocol != IP_PROTO_TCP)
399 ip_src_addr = *((guint32 *) (buf_ip + 12));
400 ip_dst_addr = *((guint32 *) (buf_ip + 16));
401 buf_proto = buf_ip + ip_header_size;
403 /* ok for tcp and udp */
404 src_port = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 0)));
405 dst_port = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 2)));
407 /* extract some params and data according to protocol */
408 if (ip_protocol == IP_PROTO_UDP) {
409 len = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 4)));
410 if (len < UDP_HEADER_LEN || buf_proto + len > buf + buf_size)
413 *payload = buf_proto + UDP_HEADER_LEN;
414 *payload_size = len - UDP_HEADER_LEN;
416 if (buf_proto + 12 >= buf + buf_size)
418 len = (buf_proto[12] >> 4) * 4;
419 if (buf_proto + len > buf + buf_size)
422 /* all remaining data following tcp header is payload */
423 *payload = buf_proto + len;
424 *payload_size = self->cur_packet_size - (buf_proto - buf) - len;
427 /* but still filter as configured */
428 if (self->src_ip >= 0 && ip_src_addr != self->src_ip)
431 if (self->dst_ip >= 0 && ip_dst_addr != self->dst_ip)
434 if (self->src_port >= 0 && src_port != self->src_port)
437 if (self->dst_port >= 0 && dst_port != self->dst_port)
444 gst_pcap_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
446 GstPcapParse *self = GST_PCAP_PARSE (parent);
447 GstFlowReturn ret = GST_FLOW_OK;
449 gst_adapter_push (self->adapter, buffer);
451 while (ret == GST_FLOW_OK) {
455 avail = gst_adapter_available (self->adapter);
457 if (self->initialized) {
458 if (self->cur_packet_size >= 0) {
459 if (avail < self->cur_packet_size)
462 if (self->cur_packet_size > 0) {
463 const guint8 *payload_data;
466 data = gst_adapter_map (self->adapter, self->cur_packet_size);
468 GST_LOG_OBJECT (self, "examining packet size %" G_GINT64_FORMAT,
469 self->cur_packet_size);
471 if (gst_pcap_parse_scan_frame (self, data, self->cur_packet_size,
472 &payload_data, &payload_size)) {
476 out_buf = gst_buffer_new_and_alloc (payload_size);
479 if (GST_CLOCK_TIME_IS_VALID (self->cur_ts)) {
480 if (!GST_CLOCK_TIME_IS_VALID (self->base_ts))
481 self->base_ts = self->cur_ts;
482 if (self->offset >= 0) {
483 self->cur_ts -= self->base_ts;
484 self->cur_ts += self->offset;
488 data = gst_buffer_map (out_buf, NULL, NULL, GST_MAP_WRITE);
489 memcpy (data, payload_data, payload_size);
490 gst_buffer_unmap (out_buf, data, -1);
491 GST_BUFFER_TIMESTAMP (out_buf) = self->cur_ts;
493 if (!self->newsegment_sent &&
494 GST_CLOCK_TIME_IS_VALID (self->cur_ts)) {
497 gst_pad_set_caps (self->src_pad, self->caps);
498 gst_segment_init (&segment, GST_FORMAT_TIME);
499 segment.start = self->cur_ts;
500 gst_pad_push_event (self->src_pad,
501 gst_event_new_segment (&segment));
502 self->newsegment_sent = TRUE;
505 ret = gst_pad_push (self->src_pad, out_buf);
507 self->buffer_offset += payload_size;
511 gst_adapter_unmap (self->adapter);
512 gst_adapter_flush (self->adapter, self->cur_packet_size);
515 self->cur_packet_size = -1;
524 data = gst_adapter_map (self->adapter, 16);
526 ts_sec = gst_pcap_parse_read_uint32 (self, data + 0);
527 ts_usec = gst_pcap_parse_read_uint32 (self, data + 4);
528 incl_len = gst_pcap_parse_read_uint32 (self, data + 8);
529 /* orig_len = gst_pcap_parse_read_uint32 (self, data + 12); */
531 gst_adapter_unmap (self->adapter);
532 gst_adapter_flush (self->adapter, 16);
534 self->cur_ts = ts_sec * GST_SECOND + ts_usec * GST_USECOND;
535 self->cur_packet_size = incl_len;
540 guint16 major_version;
545 data = gst_adapter_map (self->adapter, 24);
547 magic = *((guint32 *) data);
548 major_version = *((guint16 *) (data + 4));
549 linktype = gst_pcap_parse_read_uint32 (self, data + 20);
550 gst_adapter_unmap (self->adapter);
552 if (magic == 0xa1b2c3d4) {
553 self->swap_endian = FALSE;
554 } else if (magic == 0xd4c3b2a1) {
555 self->swap_endian = TRUE;
556 major_version = major_version << 8 | major_version >> 8;
558 GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
559 ("File is not a libpcap file, magic is %X", magic));
560 ret = GST_FLOW_ERROR;
564 if (major_version != 2) {
565 GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
566 ("File is not a libpcap major version 2, but %u", major_version));
567 ret = GST_FLOW_ERROR;
571 if (linktype != DLT_ETHER && linktype != DLT_SLL) {
572 GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
573 ("Only dumps of type Ethernet or Linux Coooked (SLL) understood,"
574 " type %d unknown", linktype));
575 ret = GST_FLOW_ERROR;
579 GST_DEBUG_OBJECT (self, "linktype %u", linktype);
580 self->linktype = linktype;
582 gst_adapter_flush (self->adapter, 24);
583 self->initialized = TRUE;
588 if (ret != GST_FLOW_OK)
589 gst_pcap_parse_reset (self);
595 gst_pcap_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
598 GstPcapParse *self = GST_PCAP_PARSE (parent);
600 switch (GST_EVENT_TYPE (event)) {
601 case GST_EVENT_SEGMENT:
602 /* Drop it, we'll replace it with our own */
603 gst_event_unref (event);
606 ret = gst_pad_push_event (self->src_pad, event);
610 gst_object_unref (self);