diff --git a/docbook/wsug_src/WSUG_chapter_telephony.adoc b/docbook/wsug_src/WSUG_chapter_telephony.adoc index 0ae73fea79..6d912c1bfc 100644 --- a/docbook/wsug_src/WSUG_chapter_telephony.adoc +++ b/docbook/wsug_src/WSUG_chapter_telephony.adoc @@ -188,6 +188,39 @@ Default value of btn:[Output Audio Rate] is btn:[Automatic]. When multiple codec In this case user must manually select one of rates in btn:[Output Audio Rate], streams will be resampled and audio export succeeds. ==== +==== RTP Decoding Settings + +RTP is carried usually in UDP packets, on random source and destination port. Therefore without "help" Wireshark can't recognize it and shows just UDP packets. Wireshark recognizes RTP streams based on VoIP signaling, e. g. based on SDP message in SIP signaling. When signaling is not captured, Wireshark shows just UDP packets. There are multiple settings which helps Wireshark to recognize RTP even there is no related signaling. + +You can use <> function from menu:Analyze[Decode As...] menu or in mouse context menu. Here you can set that traffic on specific source or destination should be decoded as RTP. You can save settings for later use. + +Use of menu:Decode As...[] menu works fine, but for many streams it is arduous. + +You can enable heuristic dissector menu:rtp_udp[] in menu:Analyze[Enabled Protocols...]. See <> for details. Once menu:rtp_udp[] is enabled, Wireshark tries every UDP packet to decode as RTP. If decoding is possible, packet (and entire UDP stream) is decoded as RTP. + +When RTP stream uses well know port, heuristic dissector ignores it. So you might miss some RTP streams. You can enable setting for udp protocol menu:Preferences[Protocols > udp > Try heuristic sub-dissectors first], see <>. In this case heuristics dissector tries to decode UDP packet even it uses well known. + +[NOTE] +==== +Take into account that heuristics is just simple "test" whether packet can be read as RTP. It can be false positive and you can see decoded as RTP more UDP packets than expected. + +When you enable menu:udp[Try heuristic sub-dissectors first], it increases possibility of false positives. If you capture all traffic in network, false positives rate can be quite high. +==== + +==== VoIP Processing Performance and Related Limits + +Processing of RTP and decoding RTP voice takes resources. There are raw estimates you can use as guidelines... + +RTP Streams window can show as many streams as found in the capture. Its performance is limited just by memory and CPU. + +RTP Player can handle 1000+ streams, but take into account that waveforms are very small in this case. + +RTP Player creates temporary file for decoding of each stream. If your OS or user has OS enforced limit for count of opened files (most of Unix/Linux systems), you can see less streams that was added to playlist. Warnings are printed on console in this case and you will see less streams in the playlist than you send to it from other tools. + +RTP Player plays audio by OS sound system and OS is responsible for mixing audio when multiple streams are played. In many cases OS sound system has limited count of mixed streams it can play/mix. RTP Player tries to handle playback failures and show warning. If it happens, just mute some streams and start playback again. + +RTP Analysis window can handle 1000+ streams, but it is difficult to use it with so many streams - it is difficult to navigate between them. It is expected that RTP Analysis window will be used for analysis of lower tens of streams. + [[ChTelVoipCalls]] @@ -251,7 +284,7 @@ streams of a selected IAX2 call along with a graph. === ISUP Messages Window -Integrated Service User Part (ISUP) protocol provides voice and non-voice signalling for telephone communications. ISUP Messages menu opens the window which shows the related statistics. The user can filter, copy or save the data into a file. +Integrated Service User Part (ISUP) protocol provides voice and non-voice signaling for telephone communications. ISUP Messages menu opens the window which shows the related statistics. The user can filter, copy or save the data into a file. [[ChTelLTE]] @@ -319,7 +352,7 @@ This menu shows MTP3 Statistics and MTP3 Summary windows. === Osmux Windows -OSmux is a multiplex protocol which benefits satellite based GSM back-haul systems by reducing the bandwidth consumption of the voice proxying (RTP-AMR) and signalling traffic. The OSmux menu opens the packet counter window with the related statistic data. The user can filter, copy or save the data into a file. +OSmux is a multiplex protocol which benefits satellite based GSM back-haul systems by reducing the bandwidth consumption of the voice proxying (RTP-AMR) and signaling traffic. The OSmux menu opens the packet counter window with the related statistic data. The user can filter, copy or save the data into a file. === RTP @@ -463,7 +496,7 @@ The Universal Computer Protocol (UCP) plays role in transferring Short Messages === H.225 Window -H.225 telecommunication protocol which is responsible for messages in call signalling and media stream packetization for packet-based multimedia communication systems. The H.225 window shows the counted messages by types and reasons. The user can filter, copy or save the data into a file. +H.225 telecommunication protocol which is responsible for messages in call signaling and media stream packetization for packet-based multimedia communication systems. The H.225 window shows the counted messages by types and reasons. The user can filter, copy or save the data into a file. [[ChTelSIPFlows]] diff --git a/sharkd_session.c b/sharkd_session.c index be322061fe..9451c645a0 100644 --- a/sharkd_session.c +++ b/sharkd_session.c @@ -2184,7 +2184,7 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count) int i; rtpstream_tapinfo_t rtp_tapinfo = - { NULL, NULL, NULL, NULL, 0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE}; + { NULL, NULL, NULL, NULL, 0, NULL, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE}; for (i = 0; i < 16; i++) { diff --git a/ui/cli/tap-rtp.c b/ui/cli/tap-rtp.c index 1e6f5add4b..75a8bf961c 100644 --- a/ui/cli/tap-rtp.c +++ b/ui/cli/tap-rtp.c @@ -44,7 +44,7 @@ static void rtpstreams_stat_draw_cb(rtpstream_tapinfo_t *tapinfo); */ static rtpstream_tapinfo_t the_tapinfo_struct = { NULL, rtpstreams_stat_draw_cb, NULL, - NULL, 0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE + NULL, 0, NULL, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE }; static void diff --git a/ui/qt/rtp_analysis_dialog.cpp b/ui/qt/rtp_analysis_dialog.cpp index c57d508f98..632f0e2bef 100644 --- a/ui/qt/rtp_analysis_dialog.cpp +++ b/ui/qt/rtp_analysis_dialog.cpp @@ -429,7 +429,7 @@ int RtpAnalysisDialog::addTabUI(tab_info_t *new_tab) new_tab->graphHorizontalLayout->setStretch(6, 1); - ui->verticalLayout_2->addLayout(new_tab->graphHorizontalLayout); + ui->layout->addLayout(new_tab->graphHorizontalLayout); return new_tab_no; } @@ -504,6 +504,7 @@ void RtpAnalysisDialog::updateWidgets() ui->actionNextProblem->setEnabled(enable_nav); if (enable_nav) { + hint.append(tr(" %1 streams, ").arg(tabs_.count() - 1)); hint.append(tr(" G: Go to packet, N: Next problem packet")); } @@ -653,10 +654,13 @@ tap_packet_status RtpAnalysisDialog::tapPacket(void *tapinfo_ptr, packet_info *p return TAP_PACKET_DONT_REDRAW; /* is it the forward direction? */ else { - for(int i=0; itabs_.count(); i++) { - tab_info_t *tab = rtp_analysis_dialog->tabs_[i]; - if (rtpstream_id_equal_pinfo_rtp_info(&(tab->stream.id),pinfo,rtpinfo)) { + // Search tab in hash key, if there are multiple tabs with same hash + QList tabs = rtp_analysis_dialog->tab_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo)); + for (int i = 0; i < tabs.size(); i++) { + tab_info_t *tab = tabs.at(i); + if (rtpstream_id_equal_pinfo_rtp_info(&tab->stream.id, pinfo, rtpinfo)) { rtp_analysis_dialog->addPacket(tab, pinfo, rtpinfo); + break; } } } @@ -930,6 +934,7 @@ void RtpAnalysisDialog::closeTab(int index) if (index != tabs_.count()) { QWidget *remove_tab = qobject_cast(ui->tabWidget->widget(index)); tab_info_t *tab = tabs_[index]; + tab_hash_.remove(rtpstream_to_hash(&tab->stream), tab); ui->tabWidget->removeTab(index); ui->streamGraph->removeGraph(tab->jitter_graph); ui->streamGraph->removeGraph(tab->diff_graph); @@ -1089,11 +1094,16 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector stream_ { int first_tab_no = -1; + setUpdatesEnabled(false); foreach(rtpstream_info_t *rtpstream, stream_infos) { bool found = false; - for(int i=0; i < tabs_.count(); i++) { - if (rtpstream_id_equal(&(tabs_[i]->stream.id), &(rtpstream->id), RTPSTREAM_ID_EQUAL_SSRC)) { + + QList tabs = tab_hash_.values(rtpstream_to_hash(rtpstream)); + for (int i = 0; i < tabs.size(); i++) { + tab_info_t *tab = tabs.at(i); + if (rtpstream_id_equal(&tab->stream.id, &rtpstream->id, RTPSTREAM_ID_EQUAL_SSRC)) { found = true; + break; } } @@ -1108,6 +1118,7 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector stream_ new_tab->delta_vals = new QVector(); tabs_ << new_tab; cur_tab_no = addTabUI(new_tab); + tab_hash_.insert(rtpstream_to_hash(rtpstream), new_tab); if (first_tab_no == -1) { first_tab_no = cur_tab_no; } @@ -1116,6 +1127,7 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector stream_ if (first_tab_no != -1) { ui->tabWidget->setCurrentIndex(first_tab_no); } + setUpdatesEnabled(true); registerTapListener("rtp", this, NULL, 0, tapReset, tapPacket, tapDraw); cap_file_.retapPackets(); removeTapListeners(); @@ -1125,13 +1137,17 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector stream_ void RtpAnalysisDialog::removeRtpStreams(QVector stream_infos _U_) { + setUpdatesEnabled(false); foreach(rtpstream_info_t *rtpstream, stream_infos) { - for(int i=0; i < tabs_.count(); i++) { - if (rtpstream_id_equal(&(tabs_[i]->stream.id), &(rtpstream->id), RTPSTREAM_ID_EQUAL_SSRC)) { - closeTab(i); + QList tabs = tab_hash_.values(rtpstream_to_hash(rtpstream)); + for (int i = 0; i < tabs.size(); i++) { + tab_info_t *tab = tabs.at(i); + if (rtpstream_id_equal(&tab->stream.id, &rtpstream->id, RTPSTREAM_ID_EQUAL_SSRC)) { + closeTab(tabs_.indexOf(tab)); } } } + setUpdatesEnabled(true); updateGraph(); } diff --git a/ui/qt/rtp_analysis_dialog.h b/ui/qt/rtp_analysis_dialog.h index b3c0cafa13..cb9016ba1e 100644 --- a/ui/qt/rtp_analysis_dialog.h +++ b/ui/qt/rtp_analysis_dialog.h @@ -115,6 +115,7 @@ private: int tab_seq; QVector tabs_; + QMultiHash tab_hash_; QPushButton *player_button_; diff --git a/ui/qt/rtp_analysis_dialog.ui b/ui/qt/rtp_analysis_dialog.ui index 33df9bfa11..1ce64d9c79 100644 --- a/ui/qt/rtp_analysis_dialog.ui +++ b/ui/qt/rtp_analysis_dialog.ui @@ -25,10 +25,37 @@ Graph - + + + + + + 0 + 200 + + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 606 + 298 + + + + + + diff --git a/ui/qt/rtp_audio_stream.cpp b/ui/qt/rtp_audio_stream.cpp index 5fc8d1c9f5..41a848ed1d 100644 --- a/ui/qt/rtp_audio_stream.cpp +++ b/ui/qt/rtp_audio_stream.cpp @@ -36,6 +36,7 @@ #include #include #include +#include // To do: // - Only allow one rtpstream_info_t per RtpAudioStream? @@ -80,7 +81,7 @@ RtpAudioStream::RtpAudioStream(QObject *parent, rtpstream_info_t *rtpstream, boo qWarning() << "Can't create temp file in " << tempname; throw -1; } - sample_file_frame_ = new QTemporaryFile(tempname, this); + sample_file_frame_ = new QBuffer(this); if (! sample_file_frame_->open(QIODevice::ReadWrite)) { // We are out of file resources delete sample_file_; @@ -179,7 +180,7 @@ void RtpAudioStream::reset(double global_start_time) if (!sample_file_->open(QIODevice::ReadWrite)) { qWarning() << "Can't create temp file in " << tempname << " during retap"; } - sample_file_frame_ = new QTemporaryFile(tempname, this); + sample_file_frame_ = new QBuffer(this); if (!sample_file_frame_->open(QIODevice::ReadWrite)) { qWarning() << "Can't create temp file in " << tempname << " during retap"; } diff --git a/ui/qt/rtp_audio_stream.h b/ui/qt/rtp_audio_stream.h index 60a5b2e4fd..5fa14ec65e 100644 --- a/ui/qt/rtp_audio_stream.h +++ b/ui/qt/rtp_audio_stream.h @@ -154,6 +154,7 @@ public: qint64 getLeadSilenceSamples() { return prepend_samples_; } qint64 getTotalSamples() { return (sample_file_->size()/(qint64)sizeof(SAMPLE)); } bool savePayload(QIODevice *file); + guint getHash() { return rtpstream_id_to_hash(&id_); } QString getIDAsQString(); signals: diff --git a/ui/qt/rtp_player_dialog.cpp b/ui/qt/rtp_player_dialog.cpp index d634b9a83b..19c7763cb0 100644 --- a/ui/qt/rtp_player_dialog.cpp +++ b/ui/qt/rtp_player_dialog.cpp @@ -8,6 +8,7 @@ */ #include +#include #include "rtp_player_dialog.h" #include @@ -149,6 +150,8 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) : , marker_stream_requested_out_rate_(0) , last_ti_(0) , listener_removed_(true) + , block_redraw_(false) + , lock_ui_(0) { ui->setupUi(this); loadGeometry(parent.width(), parent.height()); @@ -414,6 +417,7 @@ void RtpPlayerDialog::retapPackets() void RtpPlayerDialog::rescanPackets(bool rescale_axes) { + lockUI(); // Show information for a user - it can last long time... ui->hintLabel->setText("" + tr("Decoding streams...") + ""); wsApp->processEvents(); @@ -453,6 +457,7 @@ void RtpPlayerDialog::rescanPackets(bool rescale_axes) createPlot(rescale_axes); updateWidgets(); + unlockUI(); } void RtpPlayerDialog::createPlot(bool rescale_axes) @@ -600,6 +605,8 @@ void RtpPlayerDialog::createPlot(bool rescale_axes) void RtpPlayerDialog::addSingleRtpStream(rtpstream_info_t *rtpstream) { + bool found = false; + AudioRouting audio_routing = AudioRouting(AUDIO_UNMUTED, channel_mono); if (!rtpstream) return; @@ -607,23 +614,24 @@ void RtpPlayerDialog::addSingleRtpStream(rtpstream_info_t *rtpstream) // Find the RTP streams associated with this conversation. // gtk/rtp_player.c:mark_rtp_stream_to_play does this differently. - RtpAudioStream *audio_stream = NULL; - int tli_count = ui->streamTreeWidget->topLevelItemCount(); - for (int row = 0; row < tli_count; row++) { - QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); - RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value(); + QList streams = stream_hash_.values(rtpstream_to_hash(rtpstream)); + for (int i = 0; i < streams.size(); i++) { + RtpAudioStream *row_stream = streams.at(i); if (row_stream->isMatch(rtpstream)) { - audio_stream = row_stream; + found = true; break; } } - if (!audio_stream) { + int tli_count = ui->streamTreeWidget->topLevelItemCount(); + + if (!found) { try { - audio_stream = new RtpAudioStream(this, rtpstream, stereo_available_); + RtpAudioStream *audio_stream = new RtpAudioStream(this, rtpstream, stereo_available_); audio_stream->setColor(ColorUtils::graphColor(tli_count)); QTreeWidgetItem *ti = new RtpPlayerTreeWidgetItem(ui->streamTreeWidget); + stream_hash_.insert(rtpstream_to_hash(rtpstream), audio_stream); ti->setText(src_addr_col_, address_to_qstring(&rtpstream->id.src_addr)); ti->setText(src_port_col_, QString::number(rtpstream->id.src_port)); ti->setText(dst_addr_col_, address_to_qstring(&rtpstream->id.dst_addr)); @@ -686,15 +694,19 @@ void RtpPlayerDialog::addSingleRtpStream(rtpstream_info_t *rtpstream) void RtpPlayerDialog::lockUI() { - if (playing_streams_.count() > 0) { - on_stopButton_clicked(); + if (0 == lock_ui_++) { + if (playing_streams_.count() > 0) { + on_stopButton_clicked(); + } + setEnabled(false); } - setEnabled(false); } void RtpPlayerDialog::unlockUI() { - setEnabled(true); + if (--lock_ui_ == 0) { + setEnabled(true); + } } void RtpPlayerDialog::replaceRtpStreams(QVector stream_infos) @@ -1197,15 +1209,16 @@ tap_packet_status RtpPlayerDialog::tapPacket(void *tapinfo_ptr, packet_info *pin void RtpPlayerDialog::addPacket(packet_info *pinfo, const _rtp_info *rtpinfo) { - for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { - QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); - RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value(); - + // Search stream in hash key, if there are multiple streams with same hash + QList streams = stream_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo)); + for (int i = 0; i < streams.size(); i++) { + RtpAudioStream *row_stream = streams.at(i); if (row_stream->isMatch(pinfo, rtpinfo)) { row_stream->addRtpPacket(pinfo, rtpinfo); - return; + break; } } + // qDebug() << "=ap no match!" << address_to_qstring(&pinfo->src) << address_to_qstring(&pinfo->dst); } @@ -1448,8 +1461,10 @@ void RtpPlayerDialog::on_streamTreeWidget_itemSelectionChanged() ui->actionSavePayload->setEnabled(false); } - ui->audioPlot->replot(); - updateHintLabel(); + if (!block_redraw_) { + ui->audioPlot->replot(); + updateHintLabel(); + } } // Change channel audio routing if double clicked channel column @@ -1471,6 +1486,7 @@ void RtpPlayerDialog::removeRow(QTreeWidgetItem *ti) { RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value(); if (audio_stream) { + stream_hash_.remove(audio_stream->getHash(), audio_stream); ti->setData(stream_data_col_, Qt::UserRole, QVariant()); delete audio_stream; } @@ -1513,13 +1529,16 @@ void RtpPlayerDialog::on_actionRemoveStream_triggered() { QList items = ui->streamTreeWidget->selectedItems(); + block_redraw_ = true; if (last_ti_) { highlightItem(last_ti_, false); last_ti_ = NULL; } - for(int i = 0; i=0; i-- ) { removeRow(items[i]); } + block_redraw_ = false; // TODO: Recalculate legend // - Graphs used for legend could be removed above and we must add new // - If no legend is required, it should be removed @@ -1551,7 +1570,9 @@ void RtpPlayerDialog::changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting audio_graph->setSelected(ti->isSelected()); audio_graph->setMuted(audio_routing.isMuted()); - ui->audioPlot->replot(); + if (!block_redraw_) { + ui->audioPlot->replot(); + } } } @@ -1560,11 +1581,14 @@ void RtpPlayerDialog::changeAudioRouting(AudioRouting new_audio_routing) { QList items = ui->streamTreeWidget->selectedItems(); + block_redraw_ = true; for(int i = 0; iaudioPlot->replot(); updateHintLabel(); } @@ -1621,11 +1645,14 @@ void RtpPlayerDialog::on_actionAudioRoutingMuteInvert_triggered() { QList items = ui->streamTreeWidget->selectedItems(); + block_redraw_ = true; for(int i = 0; iaudioPlot->replot(); updateHintLabel(); } @@ -1695,6 +1722,7 @@ void RtpPlayerDialog::cleanupMarkerStream() void RtpPlayerDialog::on_outputDeviceComboBox_currentIndexChanged(const QString &) { + lockUI(); stereo_available_ = isStereoAvailable(); for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); @@ -1709,10 +1737,12 @@ void RtpPlayerDialog::on_outputDeviceComboBox_currentIndexChanged(const QString cleanupMarkerStream(); fillAudioRateMenu(); rescanPackets(); + unlockUI(); } void RtpPlayerDialog::on_outputAudioRate_currentIndexChanged(const QString & rate_string) { + lockUI(); // Any unconvertable string is converted to 0 => used as Automatic rate unsigned selected_rate = rate_string.toInt(); @@ -1727,6 +1757,7 @@ void RtpPlayerDialog::on_outputAudioRate_currentIndexChanged(const QString & rat marker_stream_requested_out_rate_ = selected_rate; cleanupMarkerStream(); rescanPackets(); + unlockUI(); } void RtpPlayerDialog::on_jitterSpinBox_valueChanged(double) @@ -1844,10 +1875,14 @@ bool RtpPlayerDialog::isStereoAvailable() void RtpPlayerDialog::invertSelection() { + block_redraw_ = true; for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) { QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row); ti->setSelected(!ti->isSelected()); } + block_redraw_ = false; + ui->audioPlot->replot(); + updateHintLabel(); } void RtpPlayerDialog::on_actionSelectAll_triggered() diff --git a/ui/qt/rtp_player_dialog.h b/ui/qt/rtp_player_dialog.h index 21b47f6fc1..c3f0c8e659 100644 --- a/ui/qt/rtp_player_dialog.h +++ b/ui/qt/rtp_player_dialog.h @@ -20,6 +20,7 @@ #include "rtp_audio_stream.h" #include +#include #include #include #include @@ -177,6 +178,9 @@ private: QTreeWidgetItem *last_ti_; bool listener_removed_; QPushButton *export_btn_; + QMultiHash stream_hash_; + bool block_redraw_; + int lock_ui_; // const QString streamKey(const rtpstream_info_t *rtpstream); // const QString streamKey(const packet_info *pinfo, const struct _rtp_info *rtpinfo); diff --git a/ui/rtp_stream.c b/ui/rtp_stream.c index 48ca001da4..ddbf514d22 100644 --- a/ui/rtp_stream.c +++ b/ui/rtp_stream.c @@ -138,7 +138,6 @@ void rtpstream_mark(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file, rtpstr remove_tap_listener_rtpstream(tapinfo); } - /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/ui/rtp_stream.h b/ui/rtp_stream.h index d92af1267a..24b14b83c8 100644 --- a/ui/rtp_stream.h +++ b/ui/rtp_stream.h @@ -90,6 +90,9 @@ struct _rtpstream_tapinfo { void *tap_data; /**< data for tap callbacks */ int nstreams; /**< number of streams in the list */ GList *strinfo_list; /**< list of rtpstream_info_t* */ + GHashTable *strinfo_hash; /**< multihash of rtpstream_info_t **/ + /* multihash means that there can be */ + /* more values related to one hash key */ int npackets; /**< total number of rtp packets of all streams */ /* used while tapping. user shouldn't modify these */ tap_mode_t mode; diff --git a/ui/rtp_stream_id.c b/ui/rtp_stream_id.c index a238d900b7..9bfdd15f07 100644 --- a/ui/rtp_stream_id.c +++ b/ui/rtp_stream_id.c @@ -65,6 +65,26 @@ void rtpstream_id_free(rtpstream_id_t *id) memset(id, 0, sizeof(*id)); } +/****************************************************************************/ +/* convert rtpstream_id_t to hash */ +guint rtpstream_id_to_hash(const rtpstream_id_t *id) +{ + guint hash = 0; + + if (!id) { return 0; } + /* XOR of: */ + /* SRC PORT | DST_PORT */ + /* SSRC */ + /* SRC ADDR */ + /* DST ADDR */ + hash ^= id->src_port | id->dst_port << 16; + hash ^= id->ssrc; + add_address_to_hash(hash, &id->src_addr); + add_address_to_hash(hash, &id->dst_addr); + + return hash; +} + /****************************************************************************/ /* compare two ids by flags */ gboolean rtpstream_id_equal(const rtpstream_id_t *id1, const rtpstream_id_t *id2, guint flags) @@ -104,6 +124,27 @@ gboolean rtpstream_id_equal_pinfo_rtp_info(const rtpstream_id_t *id, const packe return FALSE; } +/****************************************************************************/ +/* convert packet_info and _rtp_info to hash */ +guint pinfo_rtp_info_to_hash(const packet_info *pinfo, const struct _rtp_info *rtp_info) +{ + guint hash = 0; + + if (!pinfo || !rtp_info) { return 0; } + /* XOR of: */ + /* SRC PORT | DST_PORT */ + /* SSRC */ + /* SRC ADDR */ + /* DST ADDR */ + hash ^= pinfo->srcport | pinfo->destport << 16; + hash ^= rtp_info->info_sync_src; + add_address_to_hash(hash, &pinfo->src); + add_address_to_hash(hash, &pinfo->dst); + + return hash; +} + + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/ui/rtp_stream_id.h b/ui/rtp_stream_id.h index a9e57e5811..81d29cc482 100644 --- a/ui/rtp_stream_id.h +++ b/ui/rtp_stream_id.h @@ -37,6 +37,11 @@ typedef struct _rtpstream_id { guint32 ssrc; } rtpstream_id_t; +/** + * Get hash of rtpstream_id + */ +guint rtpstream_id_to_hash(const rtpstream_id_t *id); + /** * Copy rtpstream_id_t structure */ @@ -70,6 +75,11 @@ gboolean rtpstream_id_equal(const rtpstream_id_t *id1, const rtpstream_id_t *id2 */ gboolean rtpstream_id_equal_pinfo_rtp_info(const rtpstream_id_t *id, const packet_info *pinfo, const struct _rtp_info *rtp_info); +/** + * Get hash of rtpstream_id extracted from packet_info and _rtp_info + */ +guint pinfo_rtp_info_to_hash(const packet_info *pinfo, const struct _rtp_info *rtp_info); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/ui/tap-rtp-common.c b/ui/tap-rtp-common.c index baa3a6c9cc..ba6fb45501 100644 --- a/ui/tap-rtp-common.c +++ b/ui/tap-rtp-common.c @@ -147,6 +147,10 @@ void rtpstream_reset(rtpstream_tapinfo_t *tapinfo) if (tapinfo->mode == TAP_ANALYSE) { /* free the data items first */ + if (tapinfo->strinfo_hash) { + g_hash_table_foreach(tapinfo->strinfo_hash, rtpstream_info_multihash_destroy_value, NULL); + g_hash_table_destroy(tapinfo->strinfo_hash); + } list = g_list_first(tapinfo->strinfo_list); while (list) { @@ -157,6 +161,7 @@ void rtpstream_reset(rtpstream_tapinfo_t *tapinfo) } g_list_free(tapinfo->strinfo_list); tapinfo->strinfo_list = NULL; + tapinfo->strinfo_hash = NULL; tapinfo->nstreams = 0; tapinfo->npackets = 0; } @@ -366,7 +371,6 @@ tap_packet_status rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissec const struct _rtp_info *rtpinfo = (const struct _rtp_info *)arg2; rtpstream_info_t new_stream_info; rtpstream_info_t *stream_info = NULL; - GList* list; rtpdump_info_t rtpdump_info; struct _rtp_conversation_info *p_conv_data = NULL; @@ -386,15 +390,8 @@ tap_packet_status rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissec } /* check whether we already have a stream with these parameters in the list */ - list = g_list_first(tapinfo->strinfo_list); - while (list) - { - if (rtpstream_info_cmp(&new_stream_info, (rtpstream_info_t *)(list->data))==0) - { - stream_info = (rtpstream_info_t *)(list->data); /*found!*/ - break; - } - list = g_list_next(list); + if (tapinfo->strinfo_hash) { + stream_info = rtpstream_info_multihash_lookup(tapinfo->strinfo_hash, &new_stream_info); } /* not in the list? then create a new entry */ @@ -419,6 +416,10 @@ tap_packet_status rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissec stream_info = rtpstream_info_malloc_and_init(); rtpstream_info_copy_deep(stream_info, &new_stream_info); tapinfo->strinfo_list = g_list_prepend(tapinfo->strinfo_list, stream_info); + if (!tapinfo->strinfo_hash) { + tapinfo->strinfo_hash = g_hash_table_new(g_direct_hash, g_direct_equal); + } + rtpstream_info_multihash_insert(tapinfo->strinfo_hash, stream_info); } /* get RTP stats for the packet */ @@ -535,6 +536,77 @@ void rtpstream_info_calc_free(rtpstream_info_calc_t *calc) wmem_free(NULL, calc->all_payload_type_names); } +/****************************************************************************/ +/* Get hash for rtpstream_info_t */ +guint rtpstream_to_hash(gconstpointer key) +{ + if (key) { + return rtpstream_id_to_hash(&((rtpstream_info_t *)key)->id); + } else { + return 0; + } +} + +/****************************************************************************/ +/* Inserts new_stream_info to multihash if its not there */ + +void rtpstream_info_multihash_insert(GHashTable *multihash, rtpstream_info_t *new_stream_info) +{ + GList *hlist = (GList *)g_hash_table_lookup(multihash, GINT_TO_POINTER(rtpstream_to_hash(new_stream_info))); + gboolean found = FALSE; + if (hlist) { + // Key exists in hash + GList *list = g_list_first(hlist); + while (list) + { + if (rtpstream_info_cmp(new_stream_info, (rtpstream_info_t *)(list->data))==0) { + found = TRUE; + break; + } + list = g_list_next(list); + } + if (!found) { + // stream_info is not in list yet, add it + hlist = g_list_prepend(hlist, new_stream_info); + } + } else { + // No key in hash, init new list + hlist = g_list_prepend(hlist, new_stream_info); + } + g_hash_table_insert(multihash, GINT_TO_POINTER(rtpstream_to_hash(new_stream_info)), hlist); +} + +/****************************************************************************/ +/* Lookup stream_info in multihash */ + +rtpstream_info_t *rtpstream_info_multihash_lookup(GHashTable *multihash, rtpstream_info_t *stream_info) +{ + GList *hlist = (GList *)g_hash_table_lookup(multihash, GINT_TO_POINTER(rtpstream_to_hash(stream_info))); + if (hlist) { + // Key exists in hash + GList *list = g_list_first(hlist); + while (list) + { + if (rtpstream_info_cmp(stream_info, (rtpstream_info_t *)(list->data))==0) { + return (rtpstream_info_t *)(list->data); + } + list = g_list_next(list); + } + } + + // No stream_info in hash or was not found in existing list + return NULL; +} + +/****************************************************************************/ +/* Destroys GList used in multihash */ + +void rtpstream_info_multihash_destroy_value(gpointer key _U_, gpointer value, gpointer user_data _U_) +{ + g_list_free((GList *)value); +} + + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/ui/tap-rtp-common.h b/ui/tap-rtp-common.h index 651c6f6f9f..7b91784a82 100644 --- a/ui/tap-rtp-common.h +++ b/ui/tap-rtp-common.h @@ -138,6 +138,26 @@ void rtpstream_info_calculate(const rtpstream_info_t *strinfo, rtpstream_info_ca */ void rtpstream_info_calc_free(rtpstream_info_calc_t *calc); +/** + * Get hash key for rtpstream_info_t + */ +guint rtpstream_to_hash(gconstpointer key); + +/** + * Insert new_stream_info into multihash + */ +void rtpstream_info_multihash_insert(GHashTable *multihash, rtpstream_info_t *new_stream_info); + +/** + * Lookup stream_info in stream_info multihash + */ +rtpstream_info_t *rtpstream_info_multihash_lookup(GHashTable *multihash, rtpstream_info_t *stream_info); + +/** + * GHFunc () for destroying GList in multihash + */ +void rtpstream_info_multihash_destroy_value(gpointer key, gpointer value, gpointer user_data); + #ifdef __cplusplus } #endif /* __cplusplus */