aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-applemidi.c
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2012-02-27 09:18:53 +0000
committerAnders Broman <anders.broman@ericsson.com>2012-02-27 09:18:53 +0000
commit06cee2dfe39d95173d74dca842fc80427fb9397e (patch)
tree29c8e88a563915f0ce859a7180f688fc65c619d8 /epan/dissectors/packet-applemidi.c
parent95289022716bbce8a6fbaf480d830c06a22d5f11 (diff)
From Tobias Erichsen:
RTP-MIDI - dissector for transmission of MIDI-commands via RTP (RFC 4695). https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5451 svn path=/trunk/; revision=41200
Diffstat (limited to 'epan/dissectors/packet-applemidi.c')
-rw-r--r--epan/dissectors/packet-applemidi.c173
1 files changed, 136 insertions, 37 deletions
diff --git a/epan/dissectors/packet-applemidi.c b/epan/dissectors/packet-applemidi.c
index e212d9aa3e..edf355546c 100644
--- a/epan/dissectors/packet-applemidi.c
+++ b/epan/dissectors/packet-applemidi.c
@@ -1,6 +1,6 @@
/* packet-applemidi.c
* Routines for dissection of Apple network-midi session establishment.
- * Copyright 2006-2010, Tobias Erichsen <t.erichsen@gmx.de>
+ * Copyright 2006-2012, Tobias Erichsen <t.erichsen@gmx.de>
*
* $Id$
*
@@ -25,17 +25,27 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
- * Apple network-midi session establishment is a lightweight protocol for providing
- * a simple session establishment for MIDI-data sent in the form of RTP-MIDI (RFC 4695)
- * Peers recognize each other using the Apple Bonjour scheme with the service-name
- * "_apple-midi._udp", establish a connection using AppleMIDI (no official name, just
- * an abbreviation) and then send payload using RTP-MIDI. The implementation of
- * this dissector is based on the Apple implementation summary from May 6th, 2005.
+ * Apple network-midi session establishment is a lightweight protocol for
+ * providing a simple session establishment for MIDI-data sent in the form
+ * of RTP-MIDI (RFC 4695 / 6295). Peers recognize each other using the
+ * Apple Bonjour scheme with the service-name "_apple-midi._udp", establish
+ * a connection using AppleMIDI (no official name, just an abbreviation)
+ * and then send payload using RTP-MIDI. The implementation of this
+ * dissector is based on the Apple implementation summary from May 6th, 2005
+ * and the extension from August 13th, 2010.
+ *
+ * 2010-11-29
+ * - initial version of dissector
+ * 2012-02-24
+ * - implemented dynamic payloadtype support to automatically punt
+ * the decoding to the RTP-MIDI dissector via the RTP dissector
+ * - added new bitrate receive limit feature
*
* Here are some links:
*
* http://www.cs.berkeley.edu/~lazzaro/rtpmidi/
* http://www.faqs.org/rfcs/rfc4695.html
+ * http://www.faqs.org/rfcs/rfc6295.html
*/
#ifdef HAVE_CONFIG_H
@@ -46,22 +56,24 @@
#include <epan/packet.h>
#include <epan/conversation.h>
+#include "packet-rtp.h"
+
/* Definitions for protocol name during dissector-register */
#define APPLEMIDI_DISSECTOR_NAME "Apple Network-MIDI Session Protocol"
-#define APPLEMIDI_DISSECTOR_SHORTNAME "AppleMIDI"
-#define APPLEMIDI_DISSECTOR_ABBREVIATION "applemidi"
+#define APPLEMIDI_DISSECTOR_SHORTNAME "AppleMIDI"
+#define APPLEMIDI_DISSECTOR_ABBREVIATION "applemidi"
/* Signature "Magic Value" for Apple network MIDI session establishment */
#define APPLEMIDI_PROTOCOL_SIGNATURE 0xffff
/* Apple network MIDI valid commands */
#define APPLEMIDI_COMMAND_INVITATION 0x494e /* "IN" */
-#define APPLEMIDI_COMMAND_INVITATION_REJECTED 0x4e4f /* "NO" */
-#define APLLEMIDI_COMMAND_INVITATION_ACCEPTED 0x4f4b /* "OK" */
+#define APPLEMIDI_COMMAND_INVITATION_REJECTED 0x4e4f /* "NO" */
+#define APLLEMIDI_COMMAND_INVITATION_ACCEPTED 0x4f4b /* "OK" */
#define APPLEMIDI_COMMAND_ENDSESSION 0x4259 /* "BY" */
#define APPLEMIDI_COMMAND_SYNCHRONIZATION 0x434b /* "CK" */
#define APPLEMIDI_COMMAND_RECEIVER_FEEDBACK 0x5253 /* "RS" */
-
+#define APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT 0x524c /* "RL" */
static int hf_applemidi_signature = -1;
static int hf_applemidi_command = -1;
@@ -76,6 +88,8 @@ static int hf_applemidi_timestamp2 = -1;
static int hf_applemidi_timestamp3 = -1;
static int hf_applemidi_sequence_num = -1;
static int hf_applemidi_rtp_sequence_num = -1;
+static int hf_applemidi_rtp_bitrate_limit = -1;
+static int hf_applemidi_unknown_data = -1;
static gint ett_applemidi = -1;
@@ -83,22 +97,33 @@ static gint ett_applemidi_seq_num = -1;
static const value_string applemidi_commands[] = {
- { APPLEMIDI_COMMAND_INVITATION, "Invitation" },
+ { APPLEMIDI_COMMAND_INVITATION, "Invitation" },
{ APPLEMIDI_COMMAND_INVITATION_REJECTED, "Invitation Rejected" },
{ APLLEMIDI_COMMAND_INVITATION_ACCEPTED, "Invitation Accepted" },
- { APPLEMIDI_COMMAND_ENDSESSION, "End Session" },
+ { APPLEMIDI_COMMAND_ENDSESSION, "End Session" },
{ APPLEMIDI_COMMAND_SYNCHRONIZATION, "Synchronization" },
{ APPLEMIDI_COMMAND_RECEIVER_FEEDBACK, "Receiver Feedback" },
+ { APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT, "Bitrate Receive Limit" },
{ 0, NULL },
};
-static int proto_applemidi = -1;
+static int proto_applemidi = -1;
static dissector_handle_t applemidi_handle;
static dissector_handle_t rtp_handle;
+static const char applemidi_unknown_command[] = "unknown command: 0x%04x";
+
+
+static void free_encoding_name_str (void *ptr)
+{
+ encoding_name_and_rate_t *encoding_name_and_rate = (encoding_name_and_rate_t *)ptr;
+ if (encoding_name_and_rate->encoding_name) {
+ g_free(encoding_name_and_rate->encoding_name);
+ }
+}
static void
dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint16 command ) {
@@ -106,7 +131,9 @@ dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, g
guint16 seq_num;
guint8 count;
guint8 *name;
- unsigned int offset = 0;
+ gint offset = 0;
+ gint len;
+ gint string_size;
proto_tree *applemidi_tree;
proto_tree *applemidi_tree_seq_num;
@@ -116,17 +143,17 @@ dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, g
/* Clear out stuff in the info column */
col_clear( pinfo->cinfo, COL_INFO );
- col_add_fstr( pinfo->cinfo, COL_INFO, "%s", val_to_str( command, applemidi_commands, "Unknown Command: 0x%04x" ) );
+ col_add_fstr( pinfo->cinfo, COL_INFO, "%s", val_to_str( command, applemidi_commands, applemidi_unknown_command ) );
if ( tree ) {
proto_item *ti;
- ti = proto_tree_add_item( tree, proto_applemidi, tvb, 0, -1, ENC_NA );
+ ti = proto_tree_add_item( tree, proto_applemidi, tvb, 0, -1, ENC_NA );
applemidi_tree = proto_item_add_subtree( ti, ett_applemidi );
- proto_tree_add_item( applemidi_tree, hf_applemidi_signature, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_tree_add_item( applemidi_tree, hf_applemidi_signature, tvb, offset, 2, ENC_BIG_ENDIAN );
offset += 2;
- proto_tree_add_item( applemidi_tree, hf_applemidi_command, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_tree_add_item( applemidi_tree, hf_applemidi_command, tvb, offset, 2, ENC_BIG_ENDIAN );
offset += 2;
/* the format of packets for "IN", "NO", "OK" and "BY" is identical and contains
@@ -134,15 +161,14 @@ dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, g
* the SSRC that is used by the respective sides RTP-entity and optionally the
* name of the participant */
if ( ( APPLEMIDI_COMMAND_INVITATION == command ) || ( APPLEMIDI_COMMAND_INVITATION_REJECTED == command ) || ( APLLEMIDI_COMMAND_INVITATION_ACCEPTED == command ) || ( APPLEMIDI_COMMAND_ENDSESSION == command ) ) {
- int len;
- proto_tree_add_item( applemidi_tree, hf_applemidi_protocol_version, tvb, offset, 4, ENC_BIG_ENDIAN );
+ proto_tree_add_item( applemidi_tree, hf_applemidi_protocol_version, tvb, offset, 4, ENC_BIG_ENDIAN );
offset += 4;
- proto_tree_add_item( applemidi_tree, hf_applemidi_token, tvb, offset, 4, ENC_BIG_ENDIAN );
+ proto_tree_add_item( applemidi_tree, hf_applemidi_token, tvb, offset, 4, ENC_BIG_ENDIAN );
offset += 4;
- proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
+ proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
offset += 4;
len = tvb_reported_length(tvb) - offset;
@@ -150,8 +176,10 @@ dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, g
/* Name is optional */
if ( len > 0 ) {
name = tvb_get_ephemeral_string( tvb, offset, len );
- proto_tree_add_item( applemidi_tree, hf_applemidi_name, tvb, offset, len, ENC_ASCII|ENC_NA );
+ string_size = (gint)( strlen( name ) + 1 );
+ proto_tree_add_item( applemidi_tree, hf_applemidi_name, tvb, offset, string_size, ENC_UTF_8|ENC_NA );
col_append_fstr( pinfo->cinfo, COL_INFO, ": peer = \"%s\"", name );
+ offset += string_size;
}
/* the synchronization packet contains three 64bit timestamps, and a value to define how
@@ -168,7 +196,7 @@ dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, g
proto_tree_add_item( applemidi_tree, hf_applemidi_padding, tvb, offset, 3, ENC_BIG_ENDIAN );
offset += 3;
- proto_tree_add_item(applemidi_tree, hf_applemidi_timestamp1, tvb, offset, 8, ENC_BIG_ENDIAN );
+ proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp1, tvb, offset, 8, ENC_BIG_ENDIAN );
offset += 8;
proto_tree_add_item( applemidi_tree, hf_applemidi_timestamp2, tvb, offset, 8, ENC_BIG_ENDIAN );
@@ -193,12 +221,28 @@ dissect_applemidi_common( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, g
offset += 4;
col_append_fstr( pinfo->cinfo, COL_INFO, ": seq = %u", seq_num );
+ /* With the bitrate receive limit packet, the recipient can tell the sender to limit
+ the transmission to a certain bitrate. This is important if the peer is a gateway
+ to a hardware-device that only supports a certain speed. Like the MIDI 1.0 DIN-cable
+ MIDI-implementation which is limited to 31250. */
+ } else if ( APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT == command ) {
+ proto_tree_add_item( applemidi_tree, hf_applemidi_ssrc, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+
+ ti = proto_tree_add_item( applemidi_tree, hf_applemidi_rtp_bitrate_limit, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+ }
+ /* If there is any remaining data (possibly because an unknown command was encountered),
+ * we just dump it here */
+ len = tvb_length_remaining( tvb, offset );
+ if ( len > 0 ) {
+ proto_tree_add_item( applemidi_tree, hf_applemidi_unknown_data, tvb, offset, len, ENC_NA );
}
}
}
static gboolean
-test_applemidi(tvbuff_t *tvb, guint16 *command_p) {
+test_applemidi(tvbuff_t *tvb, guint16 *command_p, gboolean conversation_established ) {
*command_p = 0xffff;
@@ -208,19 +252,29 @@ test_applemidi(tvbuff_t *tvb, guint16 *command_p) {
*command_p = tvb_get_ntohs( tvb, 2 );
- /* ... followed by packet-command: "IN", "NO", "OK", "BY", "CK" and "RS" */
- if ( ( APPLEMIDI_COMMAND_INVITATION == *command_p ) ||
- ( APPLEMIDI_COMMAND_INVITATION_REJECTED == *command_p ) ||
- ( APLLEMIDI_COMMAND_INVITATION_ACCEPTED == *command_p ) ||
- ( APPLEMIDI_COMMAND_ENDSESSION == *command_p ) ||
- ( APPLEMIDI_COMMAND_SYNCHRONIZATION == *command_p ) ||
- ( APPLEMIDI_COMMAND_RECEIVER_FEEDBACK == *command_p ) )
+ /* If the conversation is establised (one prior packet with a valid known command)
+ * we won't check the commands anymore - this way we still show new commands
+ * Apple might introduct as "unknown" instead of punting to RTP-dissector */
+ if ( conversation_established ) {
+ return TRUE;
+ }
+
+
+ /* ... followed by packet-command: "IN", "NO", "OK", "BY", "CK" and "RS" and "RL" */
+ if ( ( APPLEMIDI_COMMAND_INVITATION == *command_p ) ||
+ ( APPLEMIDI_COMMAND_INVITATION_REJECTED == *command_p ) ||
+ ( APLLEMIDI_COMMAND_INVITATION_ACCEPTED == *command_p ) ||
+ ( APPLEMIDI_COMMAND_ENDSESSION == *command_p ) ||
+ ( APPLEMIDI_COMMAND_SYNCHRONIZATION == *command_p ) ||
+ ( APPLEMIDI_COMMAND_RECEIVER_FEEDBACK == *command_p ) ||
+ ( APPLEMIDI_COMMAND_BITRATE_RECEIVE_LIMIT == *command_p ) )
return TRUE;
return FALSE;
}
+
/* dissect_applemidi() is called when a packet is seen from a previously identified applemidi conversation */
/* If the packet isn't a valid applemidi packet, assume it's an RTP-MIDI packet. */
@@ -228,7 +282,7 @@ static void
dissect_applemidi( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) {
guint16 command;
- if ( test_applemidi( tvb, &command ) )
+ if ( test_applemidi( tvb, &command, TRUE ) )
dissect_applemidi_common( tvb, pinfo, tree, command );
else
call_dissector( rtp_handle, tvb, pinfo, tree );
@@ -239,18 +293,39 @@ dissect_applemidi_heur( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree ) {
guint16 command;
conversation_t *p_conv;
+ struct _rtp_conversation_info *p_conv_data = NULL;
+ encoding_name_and_rate_t *encoding_name_and_rate = NULL;
+ GHashTable *rtp_dyn_payload = NULL;
+ gint *key;
if ( tvb_length( tvb ) < 4)
return FALSE; /* not enough bytes to check */
- if ( !test_applemidi( tvb, &command ) ) {
+ if ( !test_applemidi( tvb, &command, FALSE ) ) {
return FALSE;
}
- /* call dissect_applemidi() from now on for UDP packets on this "connection" */
+ /* set dynamic payload-type 97 which is used by Apple for their RTP-MIDI implementation for this
+ address/port-tuple to cause RTP-dissector to call the RTP-MIDI-dissector for payload-decoding */
+
+ encoding_name_and_rate = g_malloc( sizeof( encoding_name_and_rate_t ) );
+ rtp_dyn_payload = g_hash_table_new_full( g_int_hash, g_int_equal, g_free, free_encoding_name_str );
+ encoding_name_and_rate->encoding_name = g_strdup( "rtp-midi" );
+ encoding_name_and_rate->sample_rate = 10000;
+ key = g_malloc( sizeof( gint ) );
+ *key = 97;
+ g_hash_table_insert( rtp_dyn_payload, key, encoding_name_and_rate );
+ rtp_add_address( pinfo, &pinfo->src, pinfo->srcport, 0, APPLEMIDI_DISSECTOR_SHORTNAME, pinfo->fd->num, FALSE, rtp_dyn_payload);
+
+ /* call dissect_applemidi() from now on for UDP packets on this "connection"
+ it is important to do this step after calling rtp_add_address, otherwise
+ all further packets will go directly to the RTP-dissector! */
+
p_conv = find_or_create_conversation(pinfo);
conversation_set_dissector( p_conv, applemidi_handle );
+ /* punt to actual decoding */
+
dissect_applemidi_common( tvb, pinfo, tree, command );
return TRUE;
@@ -417,6 +492,30 @@ proto_register_applemidi( void )
NULL, HFILL
}
},
+ {
+ &hf_applemidi_rtp_bitrate_limit,
+ {
+ "Bitrate limit",
+ "applemidi.bitrate_limit",
+ FT_UINT32,
+ BASE_DEC,
+ NULL,
+ 0x0,
+ NULL, HFILL
+ }
+ },
+ {
+ &hf_applemidi_unknown_data,
+ {
+ "Unknown Data",
+ "rtpmidi.unknown_data",
+ FT_BYTES,
+ BASE_NONE,
+ NULL,
+ 0x00,
+ NULL, HFILL
+ }
+ },
};