aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-lsc.c
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2006-07-28 21:41:53 +0000
committerAnders Broman <anders.broman@ericsson.com>2006-07-28 21:41:53 +0000
commit4203db3d4e0840de166c5fd40b8e8789cc352130 (patch)
treee5d6e1795669652412cbef76672746ee4a335de4 /epan/dissectors/packet-lsc.c
parent4f7ff62fcf946220ea969830a3e8d4441379c785 (diff)
From Sean Sheedy:
A disassembly module I wrote for Pegasus Lightweight Stream Control, a protocol used by some cable set-top boxes for video-on-demand. svn path=/trunk/; revision=18807
Diffstat (limited to 'epan/dissectors/packet-lsc.c')
-rw-r--r--epan/dissectors/packet-lsc.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/epan/dissectors/packet-lsc.c b/epan/dissectors/packet-lsc.c
new file mode 100644
index 0000000000..dd2c4b963a
--- /dev/null
+++ b/epan/dissectors/packet-lsc.c
@@ -0,0 +1,456 @@
+/* packet-lsc.c
+ * Routines for Pegasus LSC packet disassembly
+ * Copyright 2006, Sean Sheedy <seansh@users.sourceforge.net>
+ *
+ * $Id$
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/packet.h>
+#include <epan/emem.h>
+#include <epan/prefs.h>
+
+#include <packet-tcp.h>
+
+/* Forward declaration we need below */
+void proto_reg_handoff_lsc(void);
+
+#define LSC_PAUSE 0x01
+#define LSC_RESUME 0x02
+#define LSC_STATUS 0x03
+#define LSC_RESET 0x04
+#define LSC_JUMP 0x05
+#define LSC_PLAY 0x06
+#define LSC_DONE 0x40
+#define LSC_PAUSE_REPLY 0x81
+#define LSC_RESUME_REPLY 0x82
+#define LSC_STATUS_REPLY 0x83
+#define LSC_RESET_REPLY 0x84
+#define LSC_JUMP_REPLY 0x85
+#define LSC_PLAY_REPLY 0x86
+
+#define isReply(o) ((o) & 0x80)
+
+static const value_string op_code_vals[] = {
+ { LSC_PAUSE, "LSC_PAUSE" },
+ { LSC_RESUME, "LSC_RESUME" },
+ { LSC_STATUS, "LSC_STATUS" },
+ { LSC_RESET, "LSC_RESET" },
+ { LSC_JUMP, "LSC_JUMP" },
+ { LSC_PLAY, "LSC_PLAY" },
+ { LSC_DONE, "LSC_DONE" },
+ { LSC_PAUSE_REPLY, "LSC_PAUSE_REPLY" },
+ { LSC_RESUME_REPLY, "LSC_RESUME_REPLY" },
+ { LSC_STATUS_REPLY, "LSC_STATUS_REPLY" },
+ { LSC_RESET_REPLY, "LSC_RESET_REPLY" },
+ { LSC_JUMP_REPLY, "LSC_JUMP_REPLY" },
+ { LSC_PLAY_REPLY, "LSC_PLAY_REPLY" },
+ { 0, NULL }
+};
+
+#define LSC_OPCODE_LEN 3 /* Length to find op code */
+#define LSC_MIN_LEN 8 /* Minimum packet length */
+/* Length of each packet type */
+#define LSC_PAUSE_LEN 12
+#define LSC_RESUME_LEN 16
+#define LSC_STATUS_LEN 8
+#define LSC_RESET_LEN 8
+#define LSC_JUMP_LEN 20
+#define LSC_PLAY_LEN 20
+#define LSC_REPLY_LEN 17
+
+static const value_string status_code_vals[] = {
+ { 0x00, "LSC_OK" },
+ { 0x10, "LSC_BAD_REQUEST" },
+ { 0x11, "LSC_BAD_STREAM" },
+ { 0x12, "LSC_WRONG_STATE" },
+ { 0x13, "LSC_UNKNOWN" },
+ { 0x14, "LSC_NO_PERMISSION" },
+ { 0x15, "LSC_BAD_PARAM" },
+ { 0x16, "LSC_NO_IMPLEMENT" },
+ { 0x17, "LSC_NO_MEMORY" },
+ { 0x18, "LSC_IMP_LIMIT" },
+ { 0x19, "LSC_TRANSIENT" },
+ { 0x1a, "LSC_NO_RESOURCES" },
+ { 0x20, "LSC_SERVER_ERROR" },
+ { 0x21, "LSC_SERVER_FAILURE" },
+ { 0x30, "LSC_BAD_SCALE" },
+ { 0x31, "LSC_BAD_START" },
+ { 0x32, "LSC_BAD_STOP" },
+ { 0x40, "LSC_MPEG_DELIVERY" },
+ { 0, NULL }
+};
+
+static const value_string mode_vals[] = {
+ { 0x00, "O - Open Mode" },
+ { 0x01, "P - Pause Mode" },
+ { 0x02, "ST - Search Transport" },
+ { 0x03, "T - Transport" },
+ { 0x04, "TP - Transport Pause" },
+ { 0x05, "STP - Search Transport Pause" },
+ { 0x06, "PST - Pause Search Transport" },
+ { 0x07, "EOS - End of Stream" },
+ { 0, NULL }
+};
+
+/* Initialize the protocol and registered fields */
+static int proto_lsc = -1;
+static int hf_lsc_version = -1;
+static int hf_lsc_trans_id = -1;
+static int hf_lsc_op_code = -1;
+static int hf_lsc_status_code = -1;
+static int hf_lsc_stream_handle = -1;
+static int hf_lsc_start_npt = -1;
+static int hf_lsc_stop_npt = -1;
+static int hf_lsc_current_npt = -1;
+static int hf_lsc_scale_num = -1;
+static int hf_lsc_scale_denom = -1;
+static int hf_lsc_mode = -1;
+
+/* Preferences */
+static guint global_lsc_port = 0;
+static guint lsc_port = 0;
+
+
+/* Initialize the subtree pointers */
+static gint ett_lsc = -1;
+
+/* Code to actually dissect the packets */
+static void
+dissect_lsc_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *lsc_tree;
+ guint8 op_code;
+ guint32 stream;
+ guint expected_len;
+
+ /* Protocol is LSC, packet summary is not yet known */
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "LSC");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ /* Too little data? */
+ if (tvb->length < LSC_MIN_LEN)
+ {
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_set_str(pinfo->cinfo, COL_INFO, "[Too short]");
+ return;
+ }
+
+ /* Get the op code */
+ op_code = tvb_get_guint8(tvb, 2);
+ /* And the stream handle */
+ stream = tvb_get_ntohl(tvb, 4);
+
+ /* Check the data length against what we actually received */
+ switch (op_code)
+ {
+ case LSC_PAUSE:
+ expected_len = LSC_PAUSE_LEN;
+ break;
+ case LSC_RESUME:
+ expected_len = LSC_RESUME_LEN;
+ break;
+ case LSC_STATUS:
+ expected_len = LSC_STATUS_LEN;
+ break;
+ case LSC_RESET:
+ expected_len = LSC_RESET_LEN;
+ break;
+ case LSC_JUMP:
+ expected_len = LSC_JUMP_LEN;
+ break;
+ case LSC_PLAY:
+ expected_len = LSC_PLAY_LEN;
+ break;
+ case LSC_DONE:
+ case LSC_PAUSE_REPLY:
+ case LSC_RESUME_REPLY:
+ case LSC_STATUS_REPLY:
+ case LSC_RESET_REPLY:
+ case LSC_JUMP_REPLY:
+ case LSC_PLAY_REPLY:
+ expected_len = LSC_REPLY_LEN;
+ break;
+ default:
+ /* Unrecognized op code */
+ expected_len = LSC_MIN_LEN;
+ break;
+ }
+
+ /* Display the op code in the summary */
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO, "%s, session %.8u",
+ val_to_str(op_code, op_code_vals, "Unknown op code (0x%x)"),
+ stream);
+
+ if (tvb->length < expected_len)
+ col_append_str(pinfo->cinfo, COL_INFO, " [Too short]");
+ else if (tvb->length > expected_len)
+ col_append_str(pinfo->cinfo, COL_INFO, " [Too long]");
+ }
+
+ if (tree) {
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_lsc, tvb, 0, -1, FALSE);
+ lsc_tree = proto_item_add_subtree(ti, ett_lsc);
+
+ /* Add already fetched items to the tree */
+ proto_tree_add_uint(lsc_tree, hf_lsc_op_code, tvb, 2, 1, op_code);
+ proto_tree_add_uint_format_value(lsc_tree, hf_lsc_stream_handle, tvb, 4, 4,
+ stream, "%.8u", stream);
+
+ /* Add rest of LSC header */
+ proto_tree_add_uint(lsc_tree, hf_lsc_version, tvb, 0, 1,
+ tvb_get_guint8(tvb, 0));
+ proto_tree_add_uint(lsc_tree, hf_lsc_trans_id, tvb, 1, 1,
+ tvb_get_guint8(tvb, 1));
+
+ /* Only replies contain a status code */
+ if (isReply(op_code))
+ proto_tree_add_uint(lsc_tree, hf_lsc_status_code, tvb, 3, 1,
+ tvb_get_guint8(tvb, 3));
+
+ /* Add op code specific parts */
+ switch (op_code)
+ {
+ case LSC_PAUSE:
+ proto_tree_add_int(lsc_tree, hf_lsc_stop_npt, tvb, 8, 4,
+ tvb_get_ntohl(tvb, 8));
+ break;
+ case LSC_RESUME:
+ proto_tree_add_int(lsc_tree, hf_lsc_start_npt, tvb, 8, 4,
+ tvb_get_ntohl(tvb, 8));
+ proto_tree_add_int(lsc_tree, hf_lsc_scale_num, tvb, 12, 2,
+ tvb_get_ntohs(tvb, 12));
+ proto_tree_add_uint(lsc_tree, hf_lsc_scale_denom, tvb, 14, 2,
+ tvb_get_ntohs(tvb, 14));
+ break;
+ case LSC_JUMP:
+ case LSC_PLAY:
+ proto_tree_add_int(lsc_tree, hf_lsc_start_npt, tvb, 8, 4,
+ tvb_get_ntohl(tvb, 8));
+ proto_tree_add_int(lsc_tree, hf_lsc_stop_npt, tvb, 12, 4,
+ tvb_get_ntohl(tvb, 12));
+ proto_tree_add_int(lsc_tree, hf_lsc_scale_num, tvb, 16, 2,
+ tvb_get_ntohs(tvb, 16));
+ proto_tree_add_uint(lsc_tree, hf_lsc_scale_denom, tvb, 18, 2,
+ tvb_get_ntohs(tvb, 18));
+ break;
+ case LSC_DONE:
+ case LSC_PAUSE_REPLY:
+ case LSC_RESUME_REPLY:
+ case LSC_STATUS_REPLY:
+ case LSC_RESET_REPLY:
+ case LSC_JUMP_REPLY:
+ case LSC_PLAY_REPLY:
+ proto_tree_add_int(lsc_tree, hf_lsc_current_npt, tvb, 8, 4,
+ tvb_get_ntohl(tvb, 8));
+ proto_tree_add_int(lsc_tree, hf_lsc_scale_num, tvb, 12, 2,
+ tvb_get_ntohs(tvb, 12));
+ proto_tree_add_uint(lsc_tree, hf_lsc_scale_denom, tvb, 14, 2,
+ tvb_get_ntohs(tvb, 14));
+ proto_tree_add_uint(lsc_tree, hf_lsc_mode, tvb, 16, 1,
+ tvb_get_guint8(tvb, 16));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* Decode LSC over UDP */
+static void
+dissect_lsc_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ dissect_lsc_common(tvb, pinfo, tree);
+}
+
+/* Determine length of LSC message */
+static guint
+get_lsc_pdu_len(tvbuff_t *tvb, int offset)
+{
+ guint8 op_code;
+ guint pdu_len;
+
+ /* Get the op code */
+ op_code = tvb_get_guint8(tvb, offset + 2);
+
+ switch (op_code)
+ {
+ case LSC_PAUSE:
+ pdu_len = LSC_PAUSE_LEN;
+ break;
+ case LSC_RESUME:
+ pdu_len = LSC_RESUME_LEN;
+ break;
+ case LSC_STATUS:
+ pdu_len = LSC_STATUS_LEN;
+ break;
+ case LSC_RESET:
+ pdu_len = LSC_RESET_LEN;
+ break;
+ case LSC_JUMP:
+ pdu_len = LSC_JUMP_LEN;
+ break;
+ case LSC_PLAY:
+ pdu_len = LSC_PLAY_LEN;
+ break;
+ case LSC_DONE:
+ case LSC_PAUSE_REPLY:
+ case LSC_RESUME_REPLY:
+ case LSC_STATUS_REPLY:
+ case LSC_RESET_REPLY:
+ case LSC_JUMP_REPLY:
+ case LSC_PLAY_REPLY:
+ pdu_len = LSC_REPLY_LEN;
+ break;
+ default:
+ /* Unrecognized op code */
+ pdu_len = LSC_OPCODE_LEN;
+ break;
+ }
+
+ return pdu_len;
+}
+
+/* Decode LSC over TCP */
+static void
+dissect_lsc_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ tcp_dissect_pdus(tvb, pinfo, tree, TRUE, LSC_OPCODE_LEN, get_lsc_pdu_len,
+ dissect_lsc_common);
+}
+
+/* Register the protocol with Ethereal */
+void
+proto_register_lsc(void)
+{
+ module_t *lsc_module;
+
+ /* Setup list of header fields */
+ static hf_register_info hf[] = {
+ { &hf_lsc_version,
+ { "Version", "lsc.version",
+ FT_UINT8, BASE_DEC, NULL, 0,
+ "Version of the Pegasus LSC protocol", HFILL }
+ },
+ { &hf_lsc_trans_id,
+ { "Transaction ID", "lsc.trans_id",
+ FT_UINT8, BASE_DEC, NULL, 0,
+ "Transaction ID", HFILL }
+ },
+ { &hf_lsc_op_code,
+ { "Op Code", "lsc.op_code",
+ FT_UINT8, BASE_HEX, VALS(op_code_vals), 0,
+ "Operation Code", HFILL }
+ },
+ { &hf_lsc_status_code,
+ { "Status Code", "lsc.status_code",
+ FT_UINT8, BASE_HEX, VALS(status_code_vals), 0,
+ "Status Code", HFILL }
+ },
+ { &hf_lsc_stream_handle,
+ { "Stream Handle", "lsc.stream_handle",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ "Stream identification handle", HFILL }
+ },
+ { &hf_lsc_start_npt,
+ { "Start NPT", "lsc.start_npt",
+ FT_INT32, BASE_DEC, NULL, 0,
+ "Start Time (milliseconds)", HFILL }
+ },
+ { &hf_lsc_stop_npt,
+ { "Stop NPT", "lsc.stop_npt",
+ FT_INT32, BASE_DEC, NULL, 0,
+ "Stop Time (milliseconds)", HFILL }
+ },
+ { &hf_lsc_current_npt,
+ { "Current NPT", "lsc.current_npt",
+ FT_INT32, BASE_DEC, NULL, 0,
+ "Current Time (milliseconds)", HFILL }
+ },
+ { &hf_lsc_scale_num,
+ { "Scale Numerator", "lsc.scale_num",
+ FT_INT16, BASE_DEC, NULL, 0,
+ "Scale Numerator", HFILL }
+ },
+ { &hf_lsc_scale_denom,
+ { "Scale Denominator", "lsc.scale_denum",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ "Scale Denominator", HFILL }
+ },
+ { &hf_lsc_mode,
+ { "Server Mode", "lsc.mode",
+ FT_UINT8, BASE_HEX, VALS(mode_vals), 0,
+ "Current Server Mode", HFILL }
+ }
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_lsc,
+ };
+
+ /* Register the protocol name and description */
+ proto_lsc = proto_register_protocol("Pegasus Lightweight Stream Control",
+ "LSC", "lsc");
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_lsc, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ /* Register preferences module */
+ lsc_module = prefs_register_protocol(proto_lsc, proto_reg_handoff_lsc);
+
+ /* Register preferences */
+ prefs_register_uint_preference(lsc_module, "port",
+ "LSC Port",
+ "Set the TCP or UDP port for Pegasus LSC messages",
+ 10, &global_lsc_port);
+}
+
+void
+proto_reg_handoff_lsc(void)
+{
+ dissector_handle_t lsc_udp_handle;
+ dissector_handle_t lsc_tcp_handle;
+
+ lsc_udp_handle = create_dissector_handle(dissect_lsc_udp, proto_lsc);
+ lsc_tcp_handle = create_dissector_handle(dissect_lsc_tcp, proto_lsc);
+
+ /* Set the port number */
+ lsc_port = global_lsc_port;
+
+ dissector_add("udp.port", lsc_port, lsc_udp_handle);
+ dissector_add("tcp.port", lsc_port, lsc_tcp_handle);
+}